diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index ebbbb1a2a2..799591c5ce 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -26,3 +26,6 @@ c3bd8eb1214cbebbc92c7958b80aa06913bce3ba # A commit which ran Python Black on all Python files. # https://gem5-review.googlesource.com/c/public/gem5/+/47024 787204c92d876dd81357b75aede52d8ef5e053d3 + +# A commit which ran flynt all Python files. +e73655d038cdfa68964109044e33c9a6e7d85ac9 diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 931be695ba..0c158ee245 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,3 +1,121 @@ +# Version 23.0 + +This release has approximately 500 contributions from 50 unique contributors. +Below we highlight key gem5 features and improvements in this release. + +## Significant API and user-facing changes + +### Major renaming of CPU stats + +The CPU stats have been renamed. +See for details. + +Now, each stage (fetch, execute, commit) have their own stat group. +Stats that are shared between the different CPU model (O3, Minor, Simple) now have the exact same names. + +**Important:** Some stat names were misleading before this change. +With this change, stats with the same names between different CPU models have the same meaning. + +### `fs.py` and `se.py` deprecated + +These scripts have not been well supported for many gem5 releases. +With gem5 23.0, we have officially deprecated these scripts. +They have been moved into the `deprecated` directory, **but they will be removed in a future release.** +As a replacement, we strongly suggest using the gem5 standard library. +See for more information. + +### Renaming of `DEBUG` guard into `GEM5_DEBUG` + +Scons no longer defines the `DEBUG` guard in debug builds, so code making using of it should use `GEM5_DEBUG` instead. + +### Other API changes + +Also, this release: + +- Removes deprecated namespaces. Namespace names were updated a couple of releases ago. This release removes the old names. +- Uses `MemberEventWrapper` in favor of `EventWrapper` for instance member functions. +- Adds an extension mechanism to `Packet` and `Request`. +- Sets x86 CPU vendor string to "HygoneGenuine" to better support GLIBC. + +## New features and improvements + +### Large improvements to gem5 resources and gem5 resources website + +We now have a new web portal for the gem5 resources: + +This web portal will allow users to browse the resources available (e.g., disk images, kernels, workloads, binaries, simpoints, etc.) to use out-of-the-box with the gem5 standard library. +You can filter based on architecture, resource type, and compatible gem5 versions. + +For each resource, there are examples of how to use the resource and pointers to examples using the resource in the gem5 codebase. + +More information can be found on gem5's website: + +We will be expanding gem5 resources with more workloads and resources over the course of the next release. +If you would like to contribute to gem5 resources by uploading your own workloads, disk images, etc., please create an issue on GitHub. + +In addition to the new gem5 Resources web portal, the gem5 Resources API has been significantly updated and improved. +There are now much simpler functions for getting resources such as `obtain_resource()` that will download the resource by name and return a reference that can be used (e.g., as a binary in `set_se_workload` function on the board). +As such the generic `Resouce` class has been deprecated and will be removed in a future release. + +Resources are now specialized for their particular category. +For example, there is now a `BinaryResource` class which will return if a user specifies a binary resource when using the `obtain_resource` function. +This allow for resource typing and for greater resource specialization. + +### Arm ISA improvements + +Architectural support for Armv9 [Scalable Matrix extension](https://developer.arm.com/documentation/ddi0616/latest) (FEAT_SME). +The implementation employs a simple renaming scheme for the Za array register in the O3 CPU, so that writes to difference tiles in the register are considered a dependency and are therefore serialized. + +The following SVE and SIMD & FP extensions have also been implemented: +* FEAT_F64MM +* FEAT_F32MM +* FEAT_DOTPROD +* FEAT_I8MM + +And more generally: + +* FEAT_TLBIOS +* FEAT_FLAGM +* FEAT_FLAGM2 +* FEAT_RNG +* FEAT_RNG_TRAP +* FEAT_EVT + +### Support for DRAMSys + +gem5 can now use DRAMSys as a DRAM backend. + +### RISC-V improvements + +This release: + +- Fully implements RISC-V scalar cryptography extensions. +- Fully implement RISC-V rv32. +- Implements PMP lock features. +- Adds general RISC-V improvements to provide better stability. + +### Standard library improvements and new components + +This release: + +- Adds MESI_Three_Level component. +- Supports ELFies and LoopPoint analysis output from Sniper. +- Supports DRAMSys in the stdlib. + +## Bugfixes and other small improvements + +This release also: + +- Removes deprecated python libraries. +- Adds a DDR5 model. +- Adds AMD GPU MI200/gfx90a support. +- Changes building so it no longer "duplicates sources" in build/ which improves support for some IDEs and code analysis. If you still need to duplicate sources you can use the `--duplicate-sources` option to `scons`. +- Enables `--debug-activate=` to use debug trace for only a single SimObject (the opposite of `--debug-ignore`). See `--debug-help` for more information. +- Adds support to exit the simulation loop based on Arm-PMU events. +- Supports Python 3.11. +- Adds the idea of a CpuCluster to gem5. + + # Version 22.1.0.0 This release has 500 contributions from 48 unique contributors and marks our second major release of 2022. diff --git a/SConstruct b/SConstruct index e8107ea2c7..4fe2f64366 100755 --- a/SConstruct +++ b/SConstruct @@ -1,6 +1,6 @@ # -*- mode:python -*- -# Copyright (c) 2013, 2015-2020 ARM Limited +# Copyright (c) 2013, 2015-2020, 2023 ARM Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -145,6 +145,15 @@ AddOption('--gprof', action='store_true', help='Enable support for the gprof profiler') AddOption('--pprof', action='store_true', help='Enable support for the pprof profiler') +# Default to --no-duplicate-sources, but keep --duplicate-sources to opt-out +# of this new build behaviour in case it introduces regressions. We could use +# action=argparse.BooleanOptionalAction here once Python 3.9 is required. +AddOption('--duplicate-sources', action='store_true', default=False, + dest='duplicate_sources', + help='Create symlinks to sources in the build directory') +AddOption('--no-duplicate-sources', action='store_false', + dest='duplicate_sources', + help='Do not create symlinks to sources in the build directory') # Inject the built_tools directory into the python path. sys.path[1:1] = [ Dir('#build_tools').abspath ] @@ -168,6 +177,10 @@ SetOption('warn', 'no-duplicate-environment') Export('MakeAction') +# Patch re.compile to support inline flags anywhere within a RE +# string. Required to use PLY with Python 3.11+. +gem5_scons.patch_re_compile_for_inline_flags() + ######################################################################## # # Set up the main build environment. @@ -264,6 +277,8 @@ main.Append(CPPPATH=[Dir('ext')]) # Add shared top-level headers main.Prepend(CPPPATH=Dir('include')) +if not GetOption('duplicate_sources'): + main.Prepend(CPPPATH=Dir('src')) ######################################################################## @@ -290,6 +305,17 @@ main['CLANG'] = CXX_version and CXX_version.find('clang') >= 0 if main['GCC'] + main['CLANG'] > 1: error('Two compilers enabled at once?') +# Find the gem5 binary target architecture (usually host architecture). The +# "Target: " is consistent accross gcc and clang at the time of +# writting this. +bin_target_arch = readCommand([main['CXX'], '--verbose'], exception=False) +main["BIN_TARGET_ARCH"] = ( + "x86_64" + if bin_target_arch.find("Target: x86_64") != -1 + else "aarch64" + if bin_target_arch.find("Target: aarch64") != -1 + else "unknown" +) ######################################################################## # @@ -420,6 +446,8 @@ for variant_path in variant_paths: 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.", @@ -439,10 +467,6 @@ for variant_path in variant_paths: 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'): @@ -464,6 +488,17 @@ for variant_path in variant_paths: '-fno-builtin-malloc', '-fno-builtin-calloc', '-fno-builtin-realloc', '-fno-builtin-free']) + if compareVersions(env['CXXVERSION'], "9") < 0: + # `libstdc++fs`` must be explicitly linked for `std::filesystem`` + # in GCC version 8. As of GCC version 9, this is not required. + # + # In GCC 7 the `libstdc++fs`` library explicit linkage is also + # required but the `std::filesystem` is under the `experimental` + # namespace(`std::experimental::filesystem`). + # + # Note: gem5 does not support GCC versions < 7. + env.Append(LIBS=['stdc++fs']) + elif env['CLANG']: if compareVersions(env['CXXVERSION'], "6") < 0: error('clang version 6 or newer required.\n' @@ -481,6 +516,18 @@ for variant_path in variant_paths: env.Append(TCMALLOC_CCFLAGS=['-fno-builtin']) + if compareVersions(env['CXXVERSION'], "11") < 0: + # `libstdc++fs`` must be explicitly linked for `std::filesystem`` + # in clang versions 6 through 10. + # + # In addition, for these versions, the + # `std::filesystem` is under the `experimental` + # namespace(`std::experimental::filesystem`). + # + # Note: gem5 does not support clang versions < 6. + env.Append(LIBS=['stdc++fs']) + + # 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": @@ -511,7 +558,38 @@ for variant_path in variant_paths: if env['GCC'] or env['CLANG']: env.Append(CCFLAGS=['-fsanitize=%s' % sanitizers, '-fno-omit-frame-pointer'], - LINKFLAGS='-fsanitize=%s' % sanitizers) + LINKFLAGS=['-fsanitize=%s' % sanitizers, + '-static-libasan']) + + if main["BIN_TARGET_ARCH"] == "x86_64": + # Sanitizers can enlarge binary size drammatically, north of + # 2GB. This can prevent successful linkage due to symbol + # relocation outside from the 2GB region allocated by the small + # x86_64 code model that is enabled by default (32-bit relative + # offset limitation). Switching to the medium model in x86_64 + # enables 64-bit relative offset for large objects (>64KB by + # default) while sticking to 32-bit relative addressing for + # code and smaller objects. Note this comes at a potential + # performance cost so it should not be enabled in all cases. + # This should still be a very happy medium for + # non-perf-critical sanitized builds. + env.Append(CCFLAGS='-mcmodel=medium') + env.Append(LINKFLAGS='-mcmodel=medium') + elif main["BIN_TARGET_ARCH"] == "aarch64": + # aarch64 default code model is small but with different + # constrains than for x86_64. With aarch64, the small code + # model enables 4GB distance between symbols. This is + # sufficient for the largest ALL/gem5.debug target with all + # sanitizers enabled at the time of writting this. Note that + # the next aarch64 code model is "large" which prevents dynamic + # linkage so it should be avoided when possible. + pass + else: + warning( + "Unknown code model options for your architecture. " + "Linkage might fail for larger binaries " + "(e.g., ALL/gem5.debug with sanitizers enabled)." + ) else: warning("Don't know how to enable %s sanitizer(s) for your " "compiler." % sanitizers) @@ -563,9 +641,9 @@ for variant_path in variant_paths: if not GetOption('without_tcmalloc'): with gem5_scons.Configure(env) as conf: - if conf.CheckLib('tcmalloc'): + if conf.CheckLib('tcmalloc_minimal'): conf.env.Append(CCFLAGS=conf.env['TCMALLOC_CCFLAGS']) - elif conf.CheckLib('tcmalloc_minimal'): + elif conf.CheckLib('tcmalloc'): conf.env.Append(CCFLAGS=conf.env['TCMALLOC_CCFLAGS']) else: warning("You can get a 12% performance improvement by " @@ -728,11 +806,13 @@ Build variables for {dir}: 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) + exports=exports, + duplicate=GetOption('duplicate_sources')) # 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.) - SConscript('src/SConscript', variant_dir=variant_path, exports=exports) + SConscript('src/SConscript', variant_dir=variant_path, exports=exports, + duplicate=GetOption('duplicate_sources')) atexit.register(summarize_warnings) diff --git a/TESTING.md b/TESTING.md index 2273e31ea7..146aeac8b1 100644 --- a/TESTING.md +++ b/TESTING.md @@ -86,10 +86,10 @@ For instance, if you want to run only with `gem5.opt`, you can use ./main.py run --variant opt ``` -Or, if you want to just run X86 tests with the `gem5.opt` binary: +Or, if you want to just run quick tests with the `gem5.opt` binary: ```shell -./main.py run --length quick --variant opt --isa X86 +./main.py run --length quick --variant opt ``` @@ -102,6 +102,14 @@ To view all of the available tags, use The output is split into tag *types* (e.g., isa, variant, length) and the tags for each type are listed after the type name. +Note that when using the isa tag type, tests were traditionally sorted based +on what compilation it required. However, as tests have switched to all be +compiled under the ALL compilation, which includes all ISAs so one doesn't +need to compile each one individually, using the isa tag for ISAs other than +ALL has become a less optimal way of searching for tests. It would instead +be better to run subsets of tests based on their directories, as described +above. + You can specify "or" between tags within the same type by using the tag flag multiple times. For instance, to run everything that is tagged "opt" or "fast" use @@ -112,10 +120,10 @@ use You can also specify "and" between different types of tags by specifying more than one type on the command line. For instance, this will only run tests with -both the "X86" and "opt" tags. +both the "ALL" and "opt" tags. ```shell -./main.py run --isa X86 --variant opt +./main.py run --isa All --variant opt ``` ## Running tests in batch diff --git a/build_tools/cxx_config_cc.py b/build_tools/cxx_config_cc.py index a908aa8c17..33d3bba864 100644 --- a/build_tools/cxx_config_cc.py +++ b/build_tools/cxx_config_cc.py @@ -255,9 +255,7 @@ for param in sim_object._params.values(): code('} else if (name == "${{param.name}}") {') code.indent() code("${{param.name}}.clear();") - code( - "for (auto i = values.begin(); " "ret && i != values.end(); i ++)" - ) + code("for (auto i = values.begin(); ret && i != values.end(); i ++)") code("{") code.indent() code("${{param.ptype.cxx_type}} elem;") diff --git a/build_tools/debugflaghh.py b/build_tools/debugflaghh.py index 2e861e2790..1a4a379204 100644 --- a/build_tools/debugflaghh.py +++ b/build_tools/debugflaghh.py @@ -82,7 +82,6 @@ code( namespace gem5 { -GEM5_DEPRECATED_NAMESPACE(Debug, debug); namespace debug { diff --git a/build_tools/enum_cc.py b/build_tools/enum_cc.py index 476e49d750..5d82b401b2 100644 --- a/build_tools/enum_cc.py +++ b/build_tools/enum_cc.py @@ -87,7 +87,7 @@ namespace gem5 ) if enum.wrapper_is_struct: - code("const char *${wrapper_name}::${name}Strings" "[Num_${name}] =") + code("const char *${wrapper_name}::${name}Strings[Num_${name}] =") else: if enum.is_class: code( @@ -97,8 +97,7 @@ const char *${name}Strings[static_cast(${name}::Num_${name})] = ) else: code( - """GEM5_DEPRECATED_NAMESPACE(Enums, enums); -namespace enums + """namespace enums {""" ) code.indent(1) diff --git a/build_tools/marshal.py b/build_tools/marshal.py index 18afe2ca52..58c78e1632 100644 --- a/build_tools/marshal.py +++ b/build_tools/marshal.py @@ -48,7 +48,9 @@ interpretters, and so the exact same interpretter should be used both to run this script, and to read in and execute the marshalled code later. """ +import locale import marshal +import os import sys import zlib @@ -65,6 +67,11 @@ if len(sys.argv) < 4: print(f"Usage: {sys.argv[0]} CPP PY MODPATH ABSPATH", file=sys.stderr) sys.exit(1) +# Set the Python's locale settings manually based on the `LC_CTYPE` +# environment variable +if "LC_CTYPE" in os.environ: + locale.setlocale(locale.LC_CTYPE, os.environ["LC_CTYPE"]) + _, cpp, python, modpath, abspath = sys.argv with open(python, "r") as f: diff --git a/configs/common/CacheConfig.py b/configs/common/CacheConfig.py index 63ffe6765c..7a191570e3 100644 --- a/configs/common/CacheConfig.py +++ b/configs/common/CacheConfig.py @@ -60,15 +60,15 @@ def _get_hwp(hwp_option): def _get_cache_opts(level, options): opts = {} - size_attr = "{}_size".format(level) + size_attr = f"{level}_size" if hasattr(options, size_attr): opts["size"] = getattr(options, size_attr) - assoc_attr = "{}_assoc".format(level) + assoc_attr = f"{level}_assoc" if hasattr(options, assoc_attr): opts["assoc"] = getattr(options, assoc_attr) - prefetcher_attr = "{}_hwp_type".format(level) + prefetcher_attr = f"{level}_hwp_type" if hasattr(options, prefetcher_attr): opts["prefetcher"] = _get_hwp(getattr(options, prefetcher_attr)) diff --git a/configs/common/FileSystemConfig.py b/configs/common/FileSystemConfig.py index 066eb9a811..9c6647c861 100644 --- a/configs/common/FileSystemConfig.py +++ b/configs/common/FileSystemConfig.py @@ -51,7 +51,7 @@ from shutil import rmtree, copyfile def hex_mask(terms): dec_mask = reduce(operator.or_, [2**i for i in terms], 0) - return "%08x" % dec_mask + return f"{dec_mask:08x}" def file_append(path, contents): @@ -252,13 +252,13 @@ def _redirect_paths(options): # Redirect filesystem syscalls from src to the first matching dests redirect_paths = [ RedirectPath( - app_path="/proc", host_paths=["%s/fs/proc" % m5.options.outdir] + app_path="/proc", host_paths=[f"{m5.options.outdir}/fs/proc"] ), RedirectPath( - app_path="/sys", host_paths=["%s/fs/sys" % m5.options.outdir] + app_path="/sys", host_paths=[f"{m5.options.outdir}/fs/sys"] ), RedirectPath( - app_path="/tmp", host_paths=["%s/fs/tmp" % m5.options.outdir] + app_path="/tmp", host_paths=[f"{m5.options.outdir}/fs/tmp"] ), ] @@ -275,7 +275,7 @@ def _redirect_paths(options): if chroot: redirect_paths.append( RedirectPath( - app_path="/", host_paths=["%s" % os.path.expanduser(chroot)] + app_path="/", host_paths=[f"{os.path.expanduser(chroot)}"] ) ) diff --git a/configs/common/GPUTLBConfig.py b/configs/common/GPUTLBConfig.py index b70d6c5516..e59cd00da4 100644 --- a/configs/common/GPUTLBConfig.py +++ b/configs/common/GPUTLBConfig.py @@ -204,8 +204,8 @@ def config_tlb_hierarchy( # add the different TLB levels to the system # Modify here if you want to make the TLB hierarchy a child of # the shader. - exec("system.%s = TLB_array" % system_TLB_name) - exec("system.%s = Coalescer_array" % system_Coalescer_name) + exec(f"system.{system_TLB_name} = TLB_array") + exec(f"system.{system_Coalescer_name} = Coalescer_array") # =========================================================== # Specify the TLB hierarchy (i.e., port connections) diff --git a/configs/common/ObjectList.py b/configs/common/ObjectList.py index ce529677e7..4b862db9e8 100644 --- a/configs/common/ObjectList.py +++ b/configs/common/ObjectList.py @@ -65,22 +65,18 @@ class ObjectList(object): sub_cls = self._sub_classes[real_name] return sub_cls except KeyError: - print( - "{} is not a valid sub-class of {}.".format( - name, self.base_cls - ) - ) + print(f"{name} is not a valid sub-class of {self.base_cls}.") raise def print(self): """Print a list of available sub-classes and aliases.""" - print("Available {} classes:".format(self.base_cls)) + print(f"Available {self.base_cls} classes:") doc_wrapper = TextWrapper( initial_indent="\t\t", subsequent_indent="\t\t" ) for name, cls in list(self._sub_classes.items()): - print("\t{}".format(name)) + print(f"\t{name}") # Try to extract the class documentation from the class help # string. @@ -92,7 +88,7 @@ class ObjectList(object): if self._aliases: print("\Aliases:") for alias, target in list(self._aliases.items()): - print("\t{} => {}".format(alias, target)) + print(f"\t{alias} => {target}") def get_names(self): """Return a list of valid sub-class names and aliases.""" diff --git a/configs/common/Options.py b/configs/common/Options.py index 81d7791285..8344d9fd44 100644 --- a/configs/common/Options.py +++ b/configs/common/Options.py @@ -217,7 +217,7 @@ def addNoISAOptions(parser): "--maxtime", type=float, default=None, - help="Run to the specified absolute simulated time in " "seconds", + help="Run to the specified absolute simulated time in seconds", ) parser.add_argument( "-P", @@ -691,7 +691,7 @@ def addSEOptions(parser): "-o", "--options", default="", - help="""The options to pass to the binary, use " " + help="""The options to pass to the binary, use around the entire string""", ) parser.add_argument( @@ -834,8 +834,7 @@ def addFSOptions(parser): action="store", type=str, dest="benchmark", - help="Specify the benchmark to run. Available benchmarks: %s" - % DefinedBenchmarks, + help=f"Specify the benchmark to run. Available benchmarks: {DefinedBenchmarks}", ) # Metafile options diff --git a/configs/common/Simulation.py b/configs/common/Simulation.py index 731b3fcaa5..4377b65e64 100644 --- a/configs/common/Simulation.py +++ b/configs/common/Simulation.py @@ -71,7 +71,7 @@ def setCPUClass(options): TmpClass, test_mem_mode = getCPUClass(options.cpu_type) CPUClass = None if TmpClass.require_caches() and not options.caches and not options.ruby: - fatal("%s must be used with caches" % options.cpu_type) + fatal(f"{options.cpu_type} must be used with caches") if options.checkpoint_restore != None: if options.restore_with_cpu != options.cpu_type: @@ -144,7 +144,7 @@ def findCptDir(options, cptdir, testsys): fatal("Unable to find simpoint") inst += int(testsys.cpu[0].workload[0].simpoint) - checkpoint_dir = joinpath(cptdir, "cpt.%s.%s" % (options.bench, inst)) + checkpoint_dir = joinpath(cptdir, f"cpt.{options.bench}.{inst}") if not exists(checkpoint_dir): fatal("Unable to find checkpoint directory %s", checkpoint_dir) @@ -204,7 +204,7 @@ def findCptDir(options, cptdir, testsys): fatal("Checkpoint %d not found", cpt_num) cpt_starttick = int(cpts[cpt_num - 1]) - checkpoint_dir = joinpath(cptdir, "cpt.%s" % cpts[cpt_num - 1]) + checkpoint_dir = joinpath(cptdir, f"cpt.{cpts[cpt_num - 1]}") return cpt_starttick, checkpoint_dir @@ -220,7 +220,7 @@ def scriptCheckpoints(options, maxtick, cptdir): print("Creating checkpoint at inst:%d" % (checkpoint_inst)) exit_event = m5.simulate() exit_cause = exit_event.getCause() - print("exit cause = %s" % exit_cause) + print(f"exit cause = {exit_cause}") # skip checkpoint instructions should they exist while exit_cause == "checkpoint": @@ -549,10 +549,10 @@ def run(options, root, testsys, cpu_class): if options.repeat_switch: switch_class = getCPUClass(options.cpu_type)[0] if switch_class.require_caches() and not options.caches: - print("%s: Must be used with caches" % str(switch_class)) + print(f"{str(switch_class)}: Must be used with caches") sys.exit(1) if not switch_class.support_take_over(): - print("%s: CPU switching not supported" % str(switch_class)) + print(f"{str(switch_class)}: CPU switching not supported") sys.exit(1) repeat_switch_cpus = [ @@ -740,9 +740,9 @@ def run(options, root, testsys, cpu_class): ) exit_event = m5.simulate() else: - print("Switch at curTick count:%s" % str(10000)) + print(f"Switch at curTick count:{str(10000)}") exit_event = m5.simulate(10000) - print("Switched CPUS @ tick %s" % (m5.curTick())) + print(f"Switched CPUS @ tick {m5.curTick()}") m5.switchCpus(testsys, switch_cpu_list) @@ -757,7 +757,7 @@ def run(options, root, testsys, cpu_class): exit_event = m5.simulate() else: exit_event = m5.simulate(options.standard_switch) - print("Switching CPUS @ tick %s" % (m5.curTick())) + print(f"Switching CPUS @ tick {m5.curTick()}") print( "Simulation ends instruction count:%d" % (testsys.switch_cpus_1[0].max_insts_any_thread) diff --git a/configs/common/SysPaths.py b/configs/common/SysPaths.py index 7c0f5bf59b..60375c30c5 100644 --- a/configs/common/SysPaths.py +++ b/configs/common/SysPaths.py @@ -73,9 +73,7 @@ class PathSearchFunc(object): return next(p for p in paths if os.path.exists(p)) except StopIteration: raise IOError( - "Can't find file '{}' on {}.".format( - filepath, self.environment_variable - ) + f"Can't find file '{filepath}' on {self.environment_variable}." ) diff --git a/configs/common/cores/arm/HPI.py b/configs/common/cores/arm/HPI.py index c7a8127555..d3d46054f1 100644 --- a/configs/common/cores/arm/HPI.py +++ b/configs/common/cores/arm/HPI.py @@ -1420,6 +1420,7 @@ class HPI_FloatSimdFU(MinorFU): "SimdMisc", "SimdMult", "SimdMultAcc", + "SimdMatMultAcc", "SimdShift", "SimdShiftAcc", "SimdSqrt", @@ -1431,6 +1432,7 @@ class HPI_FloatSimdFU(MinorFU): "SimdFloatMisc", "SimdFloatMult", "SimdFloatMultAcc", + "SimdFloatMatMultAcc", "SimdFloatSqrt", ] ) diff --git a/configs/common/cores/arm/O3_ARM_v7a.py b/configs/common/cores/arm/O3_ARM_v7a.py index 77dc4e42a4..6a1734235a 100644 --- a/configs/common/cores/arm/O3_ARM_v7a.py +++ b/configs/common/cores/arm/O3_ARM_v7a.py @@ -53,6 +53,7 @@ class O3_ARM_v7a_FP(FUDesc): OpDesc(opClass="SimdMisc", opLat=3), OpDesc(opClass="SimdMult", opLat=5), OpDesc(opClass="SimdMultAcc", opLat=5), + OpDesc(opClass="SimdMatMultAcc", opLat=5), OpDesc(opClass="SimdShift", opLat=3), OpDesc(opClass="SimdShiftAcc", opLat=3), OpDesc(opClass="SimdSqrt", opLat=9), @@ -64,6 +65,7 @@ class O3_ARM_v7a_FP(FUDesc): OpDesc(opClass="SimdFloatMisc", opLat=3), OpDesc(opClass="SimdFloatMult", opLat=3), OpDesc(opClass="SimdFloatMultAcc", opLat=5), + OpDesc(opClass="SimdFloatMatMultAcc", opLat=5), OpDesc(opClass="SimdFloatSqrt", opLat=9), OpDesc(opClass="FloatAdd", opLat=5), OpDesc(opClass="FloatCmp", opLat=5), diff --git a/configs/common/cores/arm/ex5_LITTLE.py b/configs/common/cores/arm/ex5_LITTLE.py index 6974837dc5..982792d2d2 100644 --- a/configs/common/cores/arm/ex5_LITTLE.py +++ b/configs/common/cores/arm/ex5_LITTLE.py @@ -56,6 +56,7 @@ class ex5_LITTLE_FP(MinorDefaultFloatSimdFU): OpDesc(opClass="SimdMisc", opLat=3), OpDesc(opClass="SimdMult", opLat=4), OpDesc(opClass="SimdMultAcc", opLat=5), + OpDesc(opClass="SimdMatMultAcc", opLat=5), OpDesc(opClass="SimdShift", opLat=3), OpDesc(opClass="SimdShiftAcc", opLat=3), OpDesc(opClass="SimdSqrt", opLat=9), @@ -67,6 +68,7 @@ class ex5_LITTLE_FP(MinorDefaultFloatSimdFU): OpDesc(opClass="SimdFloatMisc", opLat=6), OpDesc(opClass="SimdFloatMult", opLat=15), OpDesc(opClass="SimdFloatMultAcc", opLat=6), + OpDesc(opClass="SimdFloatMatMultAcc", opLat=6), OpDesc(opClass="SimdFloatSqrt", opLat=17), OpDesc(opClass="FloatAdd", opLat=8), OpDesc(opClass="FloatCmp", opLat=6), diff --git a/configs/common/cores/arm/ex5_big.py b/configs/common/cores/arm/ex5_big.py index 70af6b8414..0d4d4903cf 100644 --- a/configs/common/cores/arm/ex5_big.py +++ b/configs/common/cores/arm/ex5_big.py @@ -58,6 +58,7 @@ class ex5_big_FP(FUDesc): OpDesc(opClass="SimdMisc", opLat=3), OpDesc(opClass="SimdMult", opLat=6), OpDesc(opClass="SimdMultAcc", opLat=5), + OpDesc(opClass="SimdMatMultAcc", opLat=5), OpDesc(opClass="SimdShift", opLat=3), OpDesc(opClass="SimdShiftAcc", opLat=3), OpDesc(opClass="SimdSqrt", opLat=9), @@ -69,6 +70,7 @@ class ex5_big_FP(FUDesc): OpDesc(opClass="SimdFloatMisc", opLat=3), OpDesc(opClass="SimdFloatMult", opLat=6), OpDesc(opClass="SimdFloatMultAcc", opLat=1), + OpDesc(opClass="SimdFloatMatMultAcc", opLat=1), OpDesc(opClass="SimdFloatSqrt", opLat=9), OpDesc(opClass="FloatAdd", opLat=6), OpDesc(opClass="FloatCmp", opLat=5), diff --git a/configs/common/cpu2000.py b/configs/common/cpu2000.py index 3b1b390618..06f927cbcf 100644 --- a/configs/common/cpu2000.py +++ b/configs/common/cpu2000.py @@ -83,7 +83,7 @@ class Benchmark(object): self.args = [] if not hasattr(self.__class__, "output"): - self.output = "%s.out" % self.name + self.output = f"{self.name}.out" if not hasattr(self.__class__, "simpoint"): self.simpoint = None @@ -92,13 +92,12 @@ class Benchmark(object): func = getattr(self.__class__, input_set) except AttributeError: raise AttributeError( - "The benchmark %s does not have the %s input set" - % (self.name, input_set) + f"The benchmark {self.name} does not have the {input_set} input set" ) executable = joinpath(spec_dist, "binaries", isa, os, self.binary) if not isfile(executable): - raise AttributeError("%s not found" % executable) + raise AttributeError(f"{executable} not found") self.executable = executable # root of tree for input & output data files @@ -112,7 +111,7 @@ class Benchmark(object): self.input_set = input_set if not isdir(inputs_dir): - raise AttributeError("%s not found" % inputs_dir) + raise AttributeError(f"{inputs_dir} not found") self.inputs_dir = [inputs_dir] if isdir(all_dir): @@ -121,12 +120,12 @@ class Benchmark(object): self.outputs_dir = outputs_dir if not hasattr(self.__class__, "stdin"): - self.stdin = joinpath(inputs_dir, "%s.in" % self.name) + self.stdin = joinpath(inputs_dir, f"{self.name}.in") if not isfile(self.stdin): self.stdin = None if not hasattr(self.__class__, "stdout"): - self.stdout = joinpath(outputs_dir, "%s.out" % self.name) + self.stdout = joinpath(outputs_dir, f"{self.name}.out") if not isfile(self.stdout): self.stdout = None @@ -387,9 +386,9 @@ class mesa(Benchmark): "-frames", frames, "-meshfile", - "%s.in" % self.name, + f"{self.name}.in", "-ppmfile", - "%s.ppm" % self.name, + f"{self.name}.ppm", ] def test(self, isa, os): @@ -876,34 +875,34 @@ class vortex(Benchmark): elif isa == "sparc" or isa == "sparc32": self.endian = "bendian" else: - raise AttributeError("unknown ISA %s" % isa) + raise AttributeError(f"unknown ISA {isa}") super(vortex, self).__init__(isa, os, input_set) def test(self, isa, os): - self.args = ["%s.raw" % self.endian] + self.args = [f"{self.endian}.raw"] self.output = "vortex.out" def train(self, isa, os): - self.args = ["%s.raw" % self.endian] + self.args = [f"{self.endian}.raw"] self.output = "vortex.out" def smred(self, isa, os): - self.args = ["%s.raw" % self.endian] + self.args = [f"{self.endian}.raw"] self.output = "vortex.out" def mdred(self, isa, os): - self.args = ["%s.raw" % self.endian] + self.args = [f"{self.endian}.raw"] self.output = "vortex.out" def lgred(self, isa, os): - self.args = ["%s.raw" % self.endian] + self.args = [f"{self.endian}.raw"] self.output = "vortex.out" class vortex1(vortex): def ref(self, isa, os): - self.args = ["%s1.raw" % self.endian] + self.args = [f"{self.endian}1.raw"] self.output = "vortex1.out" self.simpoint = 271 * 100e6 @@ -911,14 +910,14 @@ class vortex1(vortex): class vortex2(vortex): def ref(self, isa, os): self.simpoint = 1024 * 100e6 - self.args = ["%s2.raw" % self.endian] + self.args = [f"{self.endian}2.raw"] self.output = "vortex2.out" class vortex3(vortex): def ref(self, isa, os): self.simpoint = 564 * 100e6 - self.args = ["%s3.raw" % self.endian] + self.args = [f"{self.endian}3.raw"] self.output = "vortex3.out" @@ -1031,8 +1030,8 @@ if __name__ == "__main__": for bench in all: for input_set in "ref", "test", "train": - print("class: %s" % bench.__name__) + print(f"class: {bench.__name__}") x = bench("x86", "linux", input_set) - print("%s: %s" % (x, input_set)) + print(f"{x}: {input_set}") pprint(x.makeProcessArgs()) print() diff --git a/configs/deprecated/example/fs.py b/configs/deprecated/example/fs.py new file mode 100644 index 0000000000..c50e3ac4cc --- /dev/null +++ b/configs/deprecated/example/fs.py @@ -0,0 +1,444 @@ +# Copyright (c) 2010-2013, 2016, 2019-2020 ARM Limited +# Copyright (c) 2020 Barkhausen Institut +# 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-2014 Mark D. Hill and David A. Wood +# Copyright (c) 2009-2011 Advanced Micro Devices, Inc. +# Copyright (c) 2006-2007 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 argparse +import sys + +import m5 +from m5.defines import buildEnv +from m5.objects import * +from m5.util import addToPath, fatal, warn +from m5.util.fdthelper import * +from gem5.isas import ISA +from gem5.runtime import get_runtime_isa + +addToPath("../../") + +from ruby import Ruby + +from common.FSConfig import * +from common.SysPaths import * +from common.Benchmarks import * +from common import Simulation +from common import CacheConfig +from common import CpuConfig +from common import MemConfig +from common import ObjectList +from common.Caches import * +from common import Options + + +def cmd_line_template(): + if args.command_line and args.command_line_file: + print( + "Error: --command-line and --command-line-file are " + "mutually exclusive" + ) + sys.exit(1) + if args.command_line: + return args.command_line + if args.command_line_file: + return open(args.command_line_file).read().strip() + return None + + +def build_test_system(np): + cmdline = cmd_line_template() + isa = get_runtime_isa() + if isa == ISA.MIPS: + test_sys = makeLinuxMipsSystem(test_mem_mode, bm[0], cmdline=cmdline) + elif isa == ISA.SPARC: + test_sys = makeSparcSystem(test_mem_mode, bm[0], cmdline=cmdline) + elif isa == ISA.RISCV: + test_sys = makeBareMetalRiscvSystem( + test_mem_mode, bm[0], cmdline=cmdline + ) + elif isa == ISA.X86: + test_sys = makeLinuxX86System( + test_mem_mode, np, bm[0], args.ruby, cmdline=cmdline + ) + elif isa == ISA.ARM: + test_sys = makeArmSystem( + test_mem_mode, + args.machine_type, + np, + bm[0], + args.dtb_filename, + bare_metal=args.bare_metal, + cmdline=cmdline, + external_memory=args.external_memory_system, + ruby=args.ruby, + vio_9p=args.vio_9p, + bootloader=args.bootloader, + ) + if args.enable_context_switch_stats_dump: + test_sys.enable_context_switch_stats_dump = True + else: + fatal("Incapable of building %s full system!", isa.name) + + # Set the cache line size for the entire system + test_sys.cache_line_size = args.cacheline_size + + # Create a top-level voltage domain + test_sys.voltage_domain = VoltageDomain(voltage=args.sys_voltage) + + # Create a source clock for the system and set the clock period + test_sys.clk_domain = SrcClockDomain( + clock=args.sys_clock, voltage_domain=test_sys.voltage_domain + ) + + # Create a CPU voltage domain + test_sys.cpu_voltage_domain = VoltageDomain() + + # Create a source clock for the CPUs and set the clock period + test_sys.cpu_clk_domain = SrcClockDomain( + clock=args.cpu_clock, voltage_domain=test_sys.cpu_voltage_domain + ) + + if buildEnv["USE_RISCV_ISA"]: + test_sys.workload.bootloader = args.kernel + elif args.kernel is not None: + test_sys.workload.object_file = binary(args.kernel) + + if args.script is not None: + test_sys.readfile = args.script + + test_sys.init_param = args.init_param + + # For now, assign all the CPUs to the same clock domain + test_sys.cpu = [ + TestCPUClass(clk_domain=test_sys.cpu_clk_domain, cpu_id=i) + for i in range(np) + ] + + if args.ruby: + bootmem = getattr(test_sys, "_bootmem", None) + Ruby.create_system( + args, True, test_sys, test_sys.iobus, test_sys._dma_ports, bootmem + ) + + # Create a seperate clock domain for Ruby + test_sys.ruby.clk_domain = SrcClockDomain( + clock=args.ruby_clock, voltage_domain=test_sys.voltage_domain + ) + + # Connect the ruby io port to the PIO bus, + # assuming that there is just one such port. + test_sys.iobus.mem_side_ports = test_sys.ruby._io_port.in_ports + + for (i, cpu) in enumerate(test_sys.cpu): + # + # Tie the cpu ports to the correct ruby system ports + # + cpu.clk_domain = test_sys.cpu_clk_domain + cpu.createThreads() + cpu.createInterruptController() + + test_sys.ruby._cpu_ports[i].connectCpuPorts(cpu) + + else: + if args.caches or args.l2cache: + # By default the IOCache runs at the system clock + test_sys.iocache = IOCache(addr_ranges=test_sys.mem_ranges) + test_sys.iocache.cpu_side = test_sys.iobus.mem_side_ports + test_sys.iocache.mem_side = test_sys.membus.cpu_side_ports + elif not args.external_memory_system: + test_sys.iobridge = Bridge( + delay="50ns", ranges=test_sys.mem_ranges + ) + test_sys.iobridge.cpu_side_port = test_sys.iobus.mem_side_ports + test_sys.iobridge.mem_side_port = test_sys.membus.cpu_side_ports + + # Sanity check + if args.simpoint_profile: + if not ObjectList.is_noncaching_cpu(TestCPUClass): + fatal("SimPoint generation should be done with atomic cpu") + if np > 1: + fatal( + "SimPoint generation not supported with more than one CPUs" + ) + + for i in range(np): + if args.simpoint_profile: + test_sys.cpu[i].addSimPointProbe(args.simpoint_interval) + if args.checker: + test_sys.cpu[i].addCheckerCpu() + if not ObjectList.is_kvm_cpu(TestCPUClass): + if args.bp_type: + bpClass = ObjectList.bp_list.get(args.bp_type) + test_sys.cpu[i].branchPred = bpClass() + if args.indirect_bp_type: + IndirectBPClass = ObjectList.indirect_bp_list.get( + args.indirect_bp_type + ) + test_sys.cpu[ + i + ].branchPred.indirectBranchPred = IndirectBPClass() + test_sys.cpu[i].createThreads() + + # If elastic tracing is enabled when not restoring from checkpoint and + # when not fast forwarding using the atomic cpu, then check that the + # TestCPUClass is DerivO3CPU or inherits from DerivO3CPU. If the check + # passes then attach the elastic trace probe. + # If restoring from checkpoint or fast forwarding, the code that does this for + # FutureCPUClass is in the Simulation module. If the check passes then the + # elastic trace probe is attached to the switch CPUs. + if ( + args.elastic_trace_en + and args.checkpoint_restore == None + and not args.fast_forward + ): + CpuConfig.config_etrace(TestCPUClass, test_sys.cpu, args) + + CacheConfig.config_cache(args, test_sys) + + MemConfig.config_mem(args, test_sys) + + if ObjectList.is_kvm_cpu(TestCPUClass) or ObjectList.is_kvm_cpu( + FutureClass + ): + # Assign KVM CPUs to their own event queues / threads. This + # has to be done after creating caches and other child objects + # since these mustn't inherit the CPU event queue. + for i, cpu in enumerate(test_sys.cpu): + # Child objects usually inherit the parent's event + # queue. Override that and use the same event queue for + # all devices. + for obj in cpu.descendants(): + obj.eventq_index = 0 + cpu.eventq_index = i + 1 + test_sys.kvm_vm = KvmVM() + + return test_sys + + +def build_drive_system(np): + # driver system CPU is always simple, so is the memory + # Note this is an assignment of a class, not an instance. + DriveCPUClass = AtomicSimpleCPU + drive_mem_mode = "atomic" + DriveMemClass = SimpleMemory + + cmdline = cmd_line_template() + if buildEnv["USE_MIPS_ISA"]: + drive_sys = makeLinuxMipsSystem(drive_mem_mode, bm[1], cmdline=cmdline) + elif buildEnv["USE_SPARC_ISA"]: + drive_sys = makeSparcSystem(drive_mem_mode, bm[1], cmdline=cmdline) + elif buildEnv["USE_X86_ISA"]: + drive_sys = makeLinuxX86System( + drive_mem_mode, np, bm[1], cmdline=cmdline + ) + elif buildEnv["USE_ARM_ISA"]: + drive_sys = makeArmSystem( + drive_mem_mode, + args.machine_type, + np, + bm[1], + args.dtb_filename, + cmdline=cmdline, + ) + + # Create a top-level voltage domain + drive_sys.voltage_domain = VoltageDomain(voltage=args.sys_voltage) + + # Create a source clock for the system and set the clock period + drive_sys.clk_domain = SrcClockDomain( + clock=args.sys_clock, voltage_domain=drive_sys.voltage_domain + ) + + # Create a CPU voltage domain + drive_sys.cpu_voltage_domain = VoltageDomain() + + # Create a source clock for the CPUs and set the clock period + drive_sys.cpu_clk_domain = SrcClockDomain( + clock=args.cpu_clock, voltage_domain=drive_sys.cpu_voltage_domain + ) + + drive_sys.cpu = DriveCPUClass( + clk_domain=drive_sys.cpu_clk_domain, cpu_id=0 + ) + drive_sys.cpu.createThreads() + drive_sys.cpu.createInterruptController() + drive_sys.cpu.connectBus(drive_sys.membus) + if args.kernel is not None: + drive_sys.workload.object_file = binary(args.kernel) + + if ObjectList.is_kvm_cpu(DriveCPUClass): + drive_sys.kvm_vm = KvmVM() + + drive_sys.iobridge = Bridge(delay="50ns", ranges=drive_sys.mem_ranges) + drive_sys.iobridge.cpu_side_port = drive_sys.iobus.mem_side_ports + drive_sys.iobridge.mem_side_port = drive_sys.membus.cpu_side_ports + + # Create the appropriate memory controllers and connect them to the + # memory bus + drive_sys.mem_ctrls = [ + DriveMemClass(range=r) for r in drive_sys.mem_ranges + ] + for i in range(len(drive_sys.mem_ctrls)): + drive_sys.mem_ctrls[i].port = drive_sys.membus.mem_side_ports + + drive_sys.init_param = args.init_param + + return drive_sys + + +warn( + "The fs.py script is deprecated. It will be removed in future releases of " + " gem5." +) + +# Add args +parser = argparse.ArgumentParser() +Options.addCommonOptions(parser) +Options.addFSOptions(parser) + +# Add the ruby specific and protocol specific args +if "--ruby" in sys.argv: + Ruby.define_options(parser) + +args = parser.parse_args() + +# system under test can be any CPU +(TestCPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args) + +# Match the memories with the CPUs, based on the options for the test system +TestMemClass = Simulation.setMemClass(args) + +if args.benchmark: + try: + bm = Benchmarks[args.benchmark] + except KeyError: + print(f"Error benchmark {args.benchmark} has not been defined.") + print(f"Valid benchmarks are: {DefinedBenchmarks}") + sys.exit(1) +else: + if args.dual: + bm = [ + SysConfig( + disks=args.disk_image, + rootdev=args.root_device, + mem=args.mem_size, + os_type=args.os_type, + ), + SysConfig( + disks=args.disk_image, + rootdev=args.root_device, + mem=args.mem_size, + os_type=args.os_type, + ), + ] + else: + bm = [ + SysConfig( + disks=args.disk_image, + rootdev=args.root_device, + mem=args.mem_size, + os_type=args.os_type, + ) + ] + +np = args.num_cpus + +test_sys = build_test_system(np) + +if len(bm) == 2: + drive_sys = build_drive_system(np) + root = makeDualRoot(True, test_sys, drive_sys, args.etherdump) +elif len(bm) == 1 and args.dist: + # This system is part of a dist-gem5 simulation + root = makeDistRoot( + test_sys, + args.dist_rank, + args.dist_size, + args.dist_server_name, + args.dist_server_port, + args.dist_sync_repeat, + args.dist_sync_start, + args.ethernet_linkspeed, + args.ethernet_linkdelay, + args.etherdump, + ) +elif len(bm) == 1: + root = Root(full_system=True, system=test_sys) +else: + print("Error I don't know how to create more than 2 systems.") + sys.exit(1) + +if ObjectList.is_kvm_cpu(TestCPUClass) or ObjectList.is_kvm_cpu(FutureClass): + # Required for running kvm on multiple host cores. + # Uses gem5's parallel event queue feature + # Note: The simulator is quite picky about this number! + root.sim_quantum = int(1e9) # 1 ms + +if args.timesync: + root.time_sync_enable = True + +if args.frame_capture: + VncServer.frame_capture = True + +if buildEnv["USE_ARM_ISA"] and not args.bare_metal and not args.dtb_filename: + if args.machine_type not in [ + "VExpress_GEM5", + "VExpress_GEM5_V1", + "VExpress_GEM5_V2", + "VExpress_GEM5_Foundation", + ]: + warn( + "Can only correctly generate a dtb for VExpress_GEM5_* " + "platforms, unless custom hardware models have been equipped " + "with generation functionality." + ) + + # Generate a Device Tree + for sysname in ("system", "testsys", "drivesys"): + if hasattr(root, sysname): + sys = getattr(root, sysname) + sys.workload.dtb_filename = os.path.join( + m5.options.outdir, f"{sysname}.dtb" + ) + sys.generateDtb(sys.workload.dtb_filename) + +if args.wait_gdb: + test_sys.workload.wait_for_remote_gdb = True + +Simulation.setWorkCountOptions(test_sys, args) +Simulation.run(args, root, test_sys, FutureClass) diff --git a/configs/deprecated/example/se.py b/configs/deprecated/example/se.py new file mode 100644 index 0000000000..8d6735903f --- /dev/null +++ b/configs/deprecated/example/se.py @@ -0,0 +1,292 @@ +# Copyright (c) 2012-2013 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) 2006-2008 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. + +# Simple test script +# +# "m5 test.py" + +import argparse +import sys +import os + +import m5 +from m5.defines import buildEnv +from m5.objects import * +from m5.params import NULL +from m5.util import addToPath, fatal, warn +from gem5.isas import ISA +from gem5.runtime import get_runtime_isa + +addToPath("../../") + +from ruby import Ruby + +from common import Options +from common import Simulation +from common import CacheConfig +from common import CpuConfig +from common import ObjectList +from common import MemConfig +from common.FileSystemConfig import config_filesystem +from common.Caches import * +from common.cpu2000 import * + + +def get_processes(args): + """Interprets provided args and returns a list of processes""" + + multiprocesses = [] + inputs = [] + outputs = [] + errouts = [] + pargs = [] + + workloads = args.cmd.split(";") + if args.input != "": + inputs = args.input.split(";") + if args.output != "": + outputs = args.output.split(";") + if args.errout != "": + errouts = args.errout.split(";") + if args.options != "": + pargs = args.options.split(";") + + idx = 0 + for wrkld in workloads: + process = Process(pid=100 + idx) + process.executable = wrkld + process.cwd = os.getcwd() + process.gid = os.getgid() + + if args.env: + with open(args.env, "r") as f: + process.env = [line.rstrip() for line in f] + + if len(pargs) > idx: + process.cmd = [wrkld] + pargs[idx].split() + else: + process.cmd = [wrkld] + + if len(inputs) > idx: + process.input = inputs[idx] + if len(outputs) > idx: + process.output = outputs[idx] + if len(errouts) > idx: + process.errout = errouts[idx] + + multiprocesses.append(process) + idx += 1 + + if args.smt: + assert args.cpu_type == "DerivO3CPU" + return multiprocesses, idx + else: + return multiprocesses, 1 + + +warn( + "The se.py script is deprecated. It will be removed in future releases of " + " gem5." +) + +parser = argparse.ArgumentParser() +Options.addCommonOptions(parser) +Options.addSEOptions(parser) + +if "--ruby" in sys.argv: + Ruby.define_options(parser) + +args = parser.parse_args() + +multiprocesses = [] +numThreads = 1 + +if args.bench: + apps = args.bench.split("-") + if len(apps) != args.num_cpus: + print("number of benchmarks not equal to set num_cpus!") + sys.exit(1) + + for app in apps: + try: + if get_runtime_isa() == ISA.ARM: + exec( + "workload = %s('arm_%s', 'linux', '%s')" + % (app, args.arm_iset, args.spec_input) + ) + else: + # TARGET_ISA has been removed, but this is missing a ], so it + # has incorrect syntax and wasn't being used anyway. + exec( + "workload = %s(buildEnv['TARGET_ISA', 'linux', '%s')" + % (app, args.spec_input) + ) + multiprocesses.append(workload.makeProcess()) + except: + print( + f"Unable to find workload for {get_runtime_isa().name()}: {app}", + file=sys.stderr, + ) + sys.exit(1) +elif args.cmd: + multiprocesses, numThreads = get_processes(args) +else: + print("No workload specified. Exiting!\n", file=sys.stderr) + sys.exit(1) + + +(CPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args) +CPUClass.numThreads = numThreads + +# Check -- do not allow SMT with multiple CPUs +if args.smt and args.num_cpus > 1: + fatal("You cannot use SMT with multiple CPUs!") + +np = args.num_cpus +mp0_path = multiprocesses[0].executable +system = System( + cpu=[CPUClass(cpu_id=i) for i in range(np)], + mem_mode=test_mem_mode, + mem_ranges=[AddrRange(args.mem_size)], + cache_line_size=args.cacheline_size, +) + +if numThreads > 1: + system.multi_thread = True + +# Create a top-level voltage domain +system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) + +# Create a source clock for the system and set the clock period +system.clk_domain = SrcClockDomain( + clock=args.sys_clock, voltage_domain=system.voltage_domain +) + +# Create a CPU voltage domain +system.cpu_voltage_domain = VoltageDomain() + +# Create a separate clock domain for the CPUs +system.cpu_clk_domain = SrcClockDomain( + clock=args.cpu_clock, voltage_domain=system.cpu_voltage_domain +) + +# If elastic tracing is enabled, then configure the cpu and attach the elastic +# trace probe +if args.elastic_trace_en: + CpuConfig.config_etrace(CPUClass, system.cpu, args) + +# All cpus belong to a common cpu_clk_domain, therefore running at a common +# frequency. +for cpu in system.cpu: + cpu.clk_domain = system.cpu_clk_domain + +if ObjectList.is_kvm_cpu(CPUClass) or ObjectList.is_kvm_cpu(FutureClass): + if buildEnv["USE_X86_ISA"]: + system.kvm_vm = KvmVM() + system.m5ops_base = 0xFFFF0000 + for process in multiprocesses: + process.useArchPT = True + process.kvmInSE = True + else: + fatal("KvmCPU can only be used in SE mode with x86") + +# Sanity check +if args.simpoint_profile: + if not ObjectList.is_noncaching_cpu(CPUClass): + fatal("SimPoint/BPProbe should be done with an atomic cpu") + if np > 1: + fatal("SimPoint generation not supported with more than one CPUs") + +for i in range(np): + if args.smt: + system.cpu[i].workload = multiprocesses + elif len(multiprocesses) == 1: + system.cpu[i].workload = multiprocesses[0] + else: + system.cpu[i].workload = multiprocesses[i] + + if args.simpoint_profile: + system.cpu[i].addSimPointProbe(args.simpoint_interval) + + if args.checker: + system.cpu[i].addCheckerCpu() + + if args.bp_type: + bpClass = ObjectList.bp_list.get(args.bp_type) + system.cpu[i].branchPred = bpClass() + + if args.indirect_bp_type: + indirectBPClass = ObjectList.indirect_bp_list.get( + args.indirect_bp_type + ) + system.cpu[i].branchPred.indirectBranchPred = indirectBPClass() + + system.cpu[i].createThreads() + +if args.ruby: + Ruby.create_system(args, False, system) + assert args.num_cpus == len(system.ruby._cpu_ports) + + system.ruby.clk_domain = SrcClockDomain( + clock=args.ruby_clock, voltage_domain=system.voltage_domain + ) + for i in range(np): + ruby_port = system.ruby._cpu_ports[i] + + # Create the interrupt controller and connect its ports to Ruby + # Note that the interrupt controller is always present but only + # in x86 does it have message ports that need to be connected + system.cpu[i].createInterruptController() + + # Connect the cpu's cache ports to Ruby + ruby_port.connectCpuPorts(system.cpu[i]) +else: + MemClass = Simulation.setMemClass(args) + system.membus = SystemXBar() + system.system_port = system.membus.cpu_side_ports + CacheConfig.config_cache(args, system) + MemConfig.config_mem(args, system) + config_filesystem(system, args) + +system.workload = SEWorkload.init_compatible(mp0_path) + +if args.wait_gdb: + system.workload.wait_for_remote_gdb = True + +root = Root(full_system=False, system=system) +Simulation.run(args, root, system, FutureClass) diff --git a/configs/example/apu_se.py b/configs/example/apu_se.py index acf527bdf7..287135fd62 100644 --- a/configs/example/apu_se.py +++ b/configs/example/apu_se.py @@ -85,7 +85,7 @@ parser.add_argument( "--cu-per-sqc", type=int, default=4, - help="number of CUs" "sharing an SQC (icache, and thus icache TLB)", + help="number of CUssharing an SQC (icache, and thus icache TLB)", ) parser.add_argument( "--cu-per-scalar-cache", @@ -94,7 +94,7 @@ parser.add_argument( help="Number of CUs sharing a scalar cache", ) parser.add_argument( - "--simds-per-cu", type=int, default=4, help="SIMD units" "per CU" + "--simds-per-cu", type=int, default=4, help="SIMD unitsper CU" ) parser.add_argument( "--cu-per-sa", @@ -140,13 +140,13 @@ parser.add_argument( "--glbmem-wr-bus-width", type=int, default=32, - help="VGPR to Coalescer (Global Memory) data bus width " "in bytes", + help="VGPR to Coalescer (Global Memory) data bus width in bytes", ) parser.add_argument( "--glbmem-rd-bus-width", type=int, default=32, - help="Coalescer to VGPR (Global Memory) data bus width in " "bytes", + help="Coalescer to VGPR (Global Memory) data bus width in bytes", ) # Currently we only support 1 local memory pipe parser.add_argument( @@ -166,7 +166,7 @@ parser.add_argument( "--wfs-per-simd", type=int, default=10, - help="Number of " "WF slots per SIMD", + help="Number of WF slots per SIMD", ) parser.add_argument( @@ -276,12 +276,25 @@ parser.add_argument( help="Latency for responses from ruby to the cu.", ) parser.add_argument( - "--TLB-prefetch", type=int, help="prefetch depth for" "TLBs" + "--scalar-mem-req-latency", + type=int, + default=50, + help="Latency for scalar requests from the cu to ruby.", ) +parser.add_argument( + "--scalar-mem-resp-latency", + type=int, + # Set to 0 as the scalar cache response path does not model + # response latency yet and this parameter is currently not used + default=0, + help="Latency for scalar responses from ruby to the cu.", +) + +parser.add_argument("--TLB-prefetch", type=int, help="prefetch depth for TLBs") parser.add_argument( "--pf-type", type=str, - help="type of prefetch: " "PF_CU, PF_WF, PF_PHASE, PF_STRIDE", + help="type of prefetch: PF_CU, PF_WF, PF_PHASE, PF_STRIDE", ) parser.add_argument("--pf-stride", type=int, help="set prefetch stride") parser.add_argument( @@ -354,7 +367,7 @@ parser.add_argument( type=str, default="gfx801", choices=GfxVersion.vals, - help="Gfx version for gpu" "Note: gfx902 is not fully supported by ROCm", + help="Gfx version for gpuNote: gfx902 is not fully supported by ROCm", ) Ruby.define_options(parser) @@ -463,6 +476,8 @@ for i in range(n_cu): vrf_lm_bus_latency=args.vrf_lm_bus_latency, mem_req_latency=args.mem_req_latency, mem_resp_latency=args.mem_resp_latency, + scalar_mem_req_latency=args.scalar_mem_req_latency, + scalar_mem_resp_latency=args.scalar_mem_resp_latency, localDataStore=LdsState( banks=args.numLdsBanks, bankConflictPenalty=args.ldsBankConflictPenalty, @@ -668,7 +683,7 @@ def find_path(base_list, rel_path, test): full_path = os.path.join(base, rel_path) if test(full_path): return full_path - fatal("%s not found in %s" % (rel_path, base_list)) + fatal(f"{rel_path} not found in {base_list}") def find_file(base_list, rel_path): @@ -702,7 +717,7 @@ else: "/usr/lib/x86_64-linux-gnu", ] ), - "HOME=%s" % os.getenv("HOME", "/"), + f"HOME={os.getenv('HOME', '/')}", # Disable the VM fault handler signal creation for dGPUs also # forces the use of DefaultSignals instead of driver-controlled # InteruptSignals throughout the runtime. DefaultSignals poll @@ -907,14 +922,10 @@ else: redirect_paths = [ RedirectPath( - app_path="/proc", host_paths=["%s/fs/proc" % m5.options.outdir] - ), - RedirectPath( - app_path="/sys", host_paths=["%s/fs/sys" % m5.options.outdir] - ), - RedirectPath( - app_path="/tmp", host_paths=["%s/fs/tmp" % m5.options.outdir] + app_path="/proc", host_paths=[f"{m5.options.outdir}/fs/proc"] ), + RedirectPath(app_path="/sys", host_paths=[f"{m5.options.outdir}/fs/sys"]), + RedirectPath(app_path="/tmp", host_paths=[f"{m5.options.outdir}/fs/tmp"]), ] system.redirect_paths = redirect_paths @@ -966,7 +977,7 @@ exit_event = m5.simulate(maxtick) if args.fast_forward: if exit_event.getCause() == "a thread reached the max instruction count": m5.switchCpus(system, switch_cpu_list) - print("Switched CPUS @ tick %s" % (m5.curTick())) + print(f"Switched CPUS @ tick {m5.curTick()}") m5.stats.reset() exit_event = m5.simulate(maxtick - m5.curTick()) elif args.fast_forward_pseudo_op: @@ -977,7 +988,7 @@ elif args.fast_forward_pseudo_op: print("Dumping stats...") m5.stats.dump() m5.switchCpus(system, switch_cpu_list) - print("Switched CPUS @ tick %s" % (m5.curTick())) + print(f"Switched CPUS @ tick {m5.curTick()}") m5.stats.reset() # This lets us switch back and forth without keeping a counter switch_cpu_list = [(x[1], x[0]) for x in switch_cpu_list] diff --git a/configs/example/arm/baremetal.py b/configs/example/arm/baremetal.py index 9eeba37ff7..08af3ef435 100644 --- a/configs/example/arm/baremetal.py +++ b/configs/example/arm/baremetal.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2017,2019-2021 ARM Limited +# Copyright (c) 2016-2017,2019-2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -44,6 +44,7 @@ import m5 from m5.util import addToPath from m5.objects import * from m5.options import * +from gem5.simulate.exit_event import ExitEvent import argparse m5.util.addToPath("../..") @@ -52,6 +53,7 @@ from common import SysPaths from common import MemConfig from common import ObjectList from common.cores.arm import HPI +from common.cores.arm import O3_ARM_v7a import devices import workloads @@ -63,8 +65,26 @@ cpu_types = { "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), + "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, + ), } +pmu_control_events = { + "enable": ExitEvent.PERF_COUNTER_ENABLE, + "disable": ExitEvent.PERF_COUNTER_DISABLE, + "reset": ExitEvent.PERF_COUNTER_RESET, +} + +pmu_interrupt_events = { + "interrupt": ExitEvent.PERF_COUNTER_INTERRUPT, +} + +pmu_stats_events = dict(**pmu_control_events, **pmu_interrupt_events) + def create_cow_image(name): """Helper function to create a Copy-on-Write disk image""" @@ -77,7 +97,7 @@ def create(args): """Create and configure the system object.""" if args.readfile and not os.path.isfile(args.readfile): - print("Error: Bootscript %s does not exist" % args.readfile) + print(f"Error: Bootscript {args.readfile} does not exist") sys.exit(1) object_file = args.kernel if args.kernel else "" @@ -122,8 +142,14 @@ def create(args): # Add CPU clusters to the system system.cpu_cluster = [ - devices.CpuCluster( - system, args.num_cores, args.cpu_freq, "1.0V", *cpu_types[args.cpu] + devices.ArmCpuCluster( + system, + args.num_cores, + args.cpu_freq, + "1.0V", + *cpu_types[args.cpu], + tarmac_gen=args.tarmac_gen, + tarmac_dest=args.tarmac_dest, ) ] @@ -136,34 +162,85 @@ def create(args): system.auto_reset_addr = True # Using GICv3 - system.realview.gic.gicv4 = False + if hasattr(system.realview.gic, "gicv4"): + system.realview.gic.gicv4 = False system.highest_el_is_64 = True workload_class = workloads.workload_list.get(args.workload) system.workload = workload_class(object_file, system) + if args.with_pmu: + enabled_pmu_events = set( + (*args.pmu_dump_stats_on, *args.pmu_reset_stats_on) + ) + exit_sim_on_control = bool( + enabled_pmu_events & set(pmu_control_events.keys()) + ) + exit_sim_on_interrupt = bool( + enabled_pmu_events & set(pmu_interrupt_events.keys()) + ) + for cluster in system.cpu_cluster: + interrupt_numbers = [args.pmu_ppi_number] * len(cluster) + cluster.addPMUs( + interrupt_numbers, + exit_sim_on_control=exit_sim_on_control, + exit_sim_on_interrupt=exit_sim_on_interrupt, + ) + + if args.exit_on_uart_eot: + for uart in system.realview.uart: + uart.end_on_eot = True + return system def run(args): cptdir = m5.options.outdir if args.checkpoint: - print("Checkpoint directory: %s" % cptdir) + print(f"Checkpoint directory: {cptdir}") + + pmu_exit_msgs = tuple(evt.value for evt in pmu_stats_events.values()) + pmu_stats_dump_msgs = tuple( + pmu_stats_events[evt].value for evt in set(args.pmu_dump_stats_on) + ) + pmu_stats_reset_msgs = tuple( + pmu_stats_events[evt].value for evt in set(args.pmu_reset_stats_on) + ) while True: event = m5.simulate() exit_msg = event.getCause() - if exit_msg == "checkpoint": - print("Dropping checkpoint at tick %d" % m5.curTick()) + if exit_msg == ExitEvent.CHECKPOINT.value: + print(f"Dropping checkpoint at tick {m5.curTick():d}") cpt_dir = os.path.join(m5.options.outdir, "cpt.%d" % m5.curTick()) m5.checkpoint(os.path.join(cpt_dir)) print("Checkpoint done.") + elif exit_msg in pmu_exit_msgs: + if exit_msg in pmu_stats_dump_msgs: + print( + f"Dumping stats at tick {m5.curTick():d}, " + f"due to {exit_msg}" + ) + m5.stats.dump() + if exit_msg in pmu_stats_reset_msgs: + print( + f"Resetting stats at tick {m5.curTick():d}, " + f"due to {exit_msg}" + ) + m5.stats.reset() else: - print(exit_msg, " @ ", m5.curTick()) + print(f"{exit_msg} ({event.getCode()}) @ {m5.curTick()}") break - sys.exit(event.getCode()) + +def arm_ppi_arg(int_num: int) -> int: + """Argparse argument parser for valid Arm PPI numbers.""" + # PPIs (1056 <= int_num <= 1119) are not yet supported by gem5 + int_num = int(int_num) + if 16 <= int_num <= 31: + return int_num + raise ValueError(f"{int_num} is not a valid Arm PPI number") def main(): @@ -230,6 +307,55 @@ def main(): ) parser.add_argument("--checkpoint", action="store_true") parser.add_argument("--restore", type=str, default=None) + parser.add_argument( + "--tarmac-gen", + action="store_true", + help="Write a Tarmac trace.", + ) + parser.add_argument( + "--tarmac-dest", + choices=TarmacDump.vals, + default="stdoutput", + help="Destination for the Tarmac trace output. [Default: stdoutput]", + ) + parser.add_argument( + "--with-pmu", + action="store_true", + help="Add a PMU to each core in the cluster.", + ) + parser.add_argument( + "--pmu-ppi-number", + type=arm_ppi_arg, + default=23, + help="The number of the PPI to use to connect each PMU to its core. " + "Must be an integer and a valid PPI number (16 <= int_num <= 31).", + ) + parser.add_argument( + "--pmu-dump-stats-on", + type=str, + default=[], + action="append", + choices=pmu_stats_events.keys(), + help="Specify the PMU events on which to dump the gem5 stats. " + "This option may be specified multiple times to enable multiple " + "PMU events.", + ) + parser.add_argument( + "--pmu-reset-stats-on", + type=str, + default=[], + action="append", + choices=pmu_stats_events.keys(), + help="Specify the PMU events on which to reset the gem5 stats. " + "This option may be specified multiple times to enable multiple " + "PMU events.", + ) + parser.add_argument( + "--exit-on-uart-eot", + action="store_true", + help="Exit simulation if any of the UARTs receive an EOT. Many " + "workloads signal termination by sending an EOT character.", + ) parser.add_argument( "--dtb-gen", action="store_true", @@ -242,25 +368,25 @@ def main(): "--semi-stdin", type=str, default="stdin", - help="Standard input for semihosting " "(default: gem5's stdin)", + help="Standard input for semihosting (default: gem5's stdin)", ) parser.add_argument( "--semi-stdout", type=str, default="stdout", - help="Standard output for semihosting " "(default: gem5's stdout)", + help="Standard output for semihosting (default: gem5's stdout)", ) parser.add_argument( "--semi-stderr", type=str, default="stderr", - help="Standard error for semihosting " "(default: gem5's stderr)", + help="Standard error for semihosting (default: gem5's stderr)", ) parser.add_argument( "--semi-path", type=str, default="", - help=("Search path for files to be loaded through " "Arm Semihosting"), + help=("Search path for files to be loaded through Arm Semihosting"), ) parser.add_argument( "args", diff --git a/configs/example/arm/devices.py b/configs/example/arm/devices.py index c6560d74dd..6c6474ca2b 100644 --- a/configs/example/arm/devices.py +++ b/configs/example/arm/devices.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2017, 2019, 2021 Arm Limited +# Copyright (c) 2016-2017, 2019, 2021-2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -95,7 +95,7 @@ class MemBus(SystemXBar): default = Self.badaddr_responder.pio -class CpuCluster(SubSystem): +class ArmCpuCluster(CpuCluster): def __init__( self, system, @@ -106,8 +106,10 @@ class CpuCluster(SubSystem): l1i_type, l1d_type, l2_type, + tarmac_gen=False, + tarmac_dest=None, ): - super(CpuCluster, self).__init__() + super().__init__() self._cpu_type = cpu_type self._l1i_type = l1i_type self._l1d_type = l1d_type @@ -120,24 +122,15 @@ class CpuCluster(SubSystem): clock=cpu_clock, voltage_domain=self.voltage_domain ) - self.cpus = [ - self._cpu_type( - cpu_id=system.numCpus() + idx, clk_domain=self.clk_domain - ) - for idx in range(num_cpus) - ] + self.generate_cpus(cpu_type, num_cpus) for cpu in self.cpus: - cpu.createThreads() - cpu.createInterruptController() - cpu.socket_id = system.numCpuClusters() - system.addCpuCluster(self, num_cpus) + if tarmac_gen: + cpu.tracer = TarmacTracer() + if tarmac_dest is not None: + cpu.tracer.outfile = tarmac_dest - def requireCaches(self): - return self._cpu_type.require_caches() - - def memoryMode(self): - return self._cpu_type.memory_mode() + system.addCpuCluster(self) def addL1(self): for cpu in self.cpus: @@ -154,7 +147,13 @@ class CpuCluster(SubSystem): cpu.connectCachedPorts(self.toL2Bus.cpu_side_ports) self.toL2Bus.mem_side_ports = self.l2.cpu_side - def addPMUs(self, ints, events=[]): + def addPMUs( + self, + ints, + events=[], + exit_sim_on_control=False, + exit_sim_on_interrupt=False, + ): """ Instantiates 1 ArmPMU per PE. The method is accepting a list of interrupt numbers (ints) used by the PMU and a list of events to @@ -166,12 +165,21 @@ class CpuCluster(SubSystem): :type ints: List[int] :param events: Additional events to be measured by the PMUs :type events: List[Union[ProbeEvent, SoftwareIncrement]] + :param exit_sim_on_control: If true, exit the sim loop when the PMU is + enabled, disabled, or reset. + :type exit_on_control: bool + :param exit_sim_on_interrupt: If true, exit the sim loop when the PMU + triggers an interrupt. + :type exit_on_control: bool + """ assert len(ints) == len(self.cpus) for cpu, pint in zip(self.cpus, ints): int_cls = ArmPPI if pint < 32 else ArmSPI for isa in cpu.isa: isa.pmu = ArmPMU(interrupt=int_cls(num=pint)) + isa.pmu.exitOnPMUControl = exit_sim_on_control + isa.pmu.exitOnPMUInterrupt = exit_sim_on_interrupt isa.pmu.addArchEvents( cpu=cpu, itb=cpu.mmu.itb, @@ -191,36 +199,63 @@ class CpuCluster(SubSystem): cpu.connectCachedPorts(bus.cpu_side_ports) -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, - ] - super(AtomicCluster, self).__init__( - system, num_cpus, cpu_clock, cpu_voltage, *cpu_config +class AtomicCluster(ArmCpuCluster): + def __init__( + self, + system, + num_cpus, + cpu_clock, + cpu_voltage="1.0V", + tarmac_gen=False, + tarmac_dest=None, + ): + super().__init__( + system, + num_cpus, + cpu_clock, + cpu_voltage, + cpu_type=ObjectList.cpu_list.get("AtomicSimpleCPU"), + l1i_type=None, + l1d_type=None, + l2_type=None, + tarmac_gen=tarmac_gen, + tarmac_dest=tarmac_dest, ) def addL1(self): pass -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] - super(KvmCluster, self).__init__( - system, num_cpus, cpu_clock, cpu_voltage, *cpu_config +class KvmCluster(ArmCpuCluster): + def __init__( + self, + system, + num_cpus, + cpu_clock, + cpu_voltage="1.0V", + tarmac_gen=False, + tarmac_dest=None, + ): + super().__init__( + system, + num_cpus, + cpu_clock, + cpu_voltage, + cpu_type=ObjectList.cpu_list.get("ArmV8KvmCPU"), + l1i_type=None, + l1d_type=None, + l2_type=None, + tarmac_gen=tarmac_gen, + tarmac_dest=tarmac_dest, ) def addL1(self): pass -class FastmodelCluster(SubSystem): +class FastmodelCluster(CpuCluster): def __init__(self, system, num_cpus, cpu_clock, cpu_voltage="1.0V"): - super(FastmodelCluster, self).__init__() + super().__init__() # Setup GIC gic = system.realview.gic @@ -285,12 +320,12 @@ class FastmodelCluster(SubSystem): self.cpu_hub.a2t = a2t self.cpu_hub.t2g = t2g - system.addCpuCluster(self, num_cpus) + system.addCpuCluster(self) - def requireCaches(self): + def require_caches(self): return False - def memoryMode(self): + def memory_mode(self): return "atomic_noncaching" def addL1(self): @@ -330,7 +365,6 @@ class BaseSimpleSystem(ArmSystem): self.mem_ranges = self.getMemRanges(int(Addr(mem_size))) self._clusters = [] - self._num_cpus = 0 def getMemRanges(self, mem_size): """ @@ -357,14 +391,8 @@ class BaseSimpleSystem(ArmSystem): def numCpuClusters(self): return len(self._clusters) - def addCpuCluster(self, cpu_cluster, num_cpus): - assert cpu_cluster not in self._clusters - assert num_cpus > 0 + def addCpuCluster(self, cpu_cluster): self._clusters.append(cpu_cluster) - self._num_cpus += num_cpus - - def numCpus(self): - return self._num_cpus def addCaches(self, need_caches, last_cache_level): if not need_caches: diff --git a/configs/example/arm/dist_bigLITTLE.py b/configs/example/arm/dist_bigLITTLE.py index a3f3ede4eb..2884a5efd5 100644 --- a/configs/example/arm/dist_bigLITTLE.py +++ b/configs/example/arm/dist_bigLITTLE.py @@ -51,7 +51,7 @@ import sw def addOptions(parser): # Options for distributed simulation (i.e. dist-gem5) parser.add_argument( - "--dist", action="store_true", help="Distributed gem5" " simulation." + "--dist", action="store_true", help="Distributed gem5 simulation." ) parser.add_argument( "--is-switch", @@ -71,14 +71,14 @@ def addOptions(parser): default=0, action="store", type=int, - help="Number of gem5 processes within the dist gem5" " run.", + help="Number of gem5 processes within the dist gem5 run.", ) parser.add_argument( "--dist-server-name", default="127.0.0.1", action="store", type=str, - help="Name of the message server host\nDEFAULT:" " localhost", + help="Name of the message server host\nDEFAULT: localhost", ) parser.add_argument( "--dist-server-port", diff --git a/configs/example/arm/fs_bigLITTLE.py b/configs/example/arm/fs_bigLITTLE.py index c188de663a..401eb0c9e7 100644 --- a/configs/example/arm/fs_bigLITTLE.py +++ b/configs/example/arm/fs_bigLITTLE.py @@ -79,7 +79,7 @@ def _using_pdes(root): return False -class BigCluster(devices.CpuCluster): +class BigCluster(devices.ArmCpuCluster): def __init__(self, system, num_cpus, cpu_clock, cpu_voltage="1.0V"): cpu_config = [ ObjectList.cpu_list.get("O3_ARM_v7a_3"), @@ -87,12 +87,10 @@ class BigCluster(devices.CpuCluster): devices.L1D, devices.L2, ] - super(BigCluster, self).__init__( - system, num_cpus, cpu_clock, cpu_voltage, *cpu_config - ) + super().__init__(system, num_cpus, cpu_clock, cpu_voltage, *cpu_config) -class LittleCluster(devices.CpuCluster): +class LittleCluster(devices.ArmCpuCluster): def __init__(self, system, num_cpus, cpu_clock, cpu_voltage="1.0V"): cpu_config = [ ObjectList.cpu_list.get("MinorCPU"), @@ -100,9 +98,7 @@ class LittleCluster(devices.CpuCluster): devices.L1D, devices.L2, ] - super(LittleCluster, self).__init__( - system, num_cpus, cpu_clock, cpu_voltage, *cpu_config - ) + super().__init__(system, num_cpus, cpu_clock, cpu_voltage, *cpu_config) class Ex5BigCluster(devices.CpuCluster): @@ -113,9 +109,7 @@ class Ex5BigCluster(devices.CpuCluster): ex5_big.L1D, ex5_big.L2, ] - super(Ex5BigCluster, self).__init__( - system, num_cpus, cpu_clock, cpu_voltage, *cpu_config - ) + super().__init__(system, num_cpus, cpu_clock, cpu_voltage, *cpu_config) class Ex5LittleCluster(devices.CpuCluster): @@ -126,9 +120,7 @@ class Ex5LittleCluster(devices.CpuCluster): ex5_LITTLE.L1D, ex5_LITTLE.L2, ] - super(Ex5LittleCluster, self).__init__( - system, num_cpus, cpu_clock, cpu_voltage, *cpu_config - ) + super().__init__(system, num_cpus, cpu_clock, cpu_voltage, *cpu_config) def createSystem( @@ -339,10 +331,10 @@ def build(options): "lpj=19988480", "norandmaps", "loglevel=8", - "mem=%s" % options.mem_size, - "root=%s" % options.root, + f"mem={options.mem_size}", + f"root={options.root}", "rw", - "init=%s" % options.kernel_init, + f"init={options.kernel_init}", "vmalloc=768MB", ] @@ -376,7 +368,7 @@ def build(options): system.bigCluster = big_model( system, options.big_cpus, options.big_cpu_clock ) - system.mem_mode = system.bigCluster.memoryMode() + system.mem_mode = system.bigCluster.memory_mode() all_cpus += system.bigCluster.cpus # little cluster @@ -384,23 +376,24 @@ def build(options): system.littleCluster = little_model( system, options.little_cpus, options.little_cpu_clock ) - system.mem_mode = system.littleCluster.memoryMode() + system.mem_mode = system.littleCluster.memory_mode() all_cpus += system.littleCluster.cpus # Figure out the memory mode if ( options.big_cpus > 0 and options.little_cpus > 0 - and system.bigCluster.memoryMode() != system.littleCluster.memoryMode() + and system.bigCluster.memory_mode() + != system.littleCluster.memory_mode() ): m5.util.panic("Memory mode missmatch among CPU clusters") # create caches system.addCaches(options.caches, options.last_cache_level) if not options.caches: - if options.big_cpus > 0 and system.bigCluster.requireCaches(): + if options.big_cpus > 0 and system.bigCluster.require_caches(): m5.util.panic("Big CPU model requires caches") - if options.little_cpus > 0 and system.littleCluster.requireCaches(): + if options.little_cpus > 0 and system.littleCluster.require_caches(): m5.util.panic("Little CPU model requires caches") # Create a KVM VM and do KVM-specific configuration diff --git a/configs/example/arm/fs_power.py b/configs/example/arm/fs_power.py index 95d2182508..671cf63f2f 100644 --- a/configs/example/arm/fs_power.py +++ b/configs/example/arm/fs_power.py @@ -79,7 +79,7 @@ class L2PowerOn(MathExprPowerModel): # Example to report l2 Cache overallAccesses # The estimated power is converted to Watt and will vary based # on the size of the cache - self.dyn = "{}.overallAccesses * 0.000018000".format(l2_path) + self.dyn = f"{l2_path}.overallAccesses * 0.000018000" self.st = "(voltage * 3)/10" diff --git a/configs/example/arm/ruby_fs.py b/configs/example/arm/ruby_fs.py index d58184522c..67a8a6e0b3 100644 --- a/configs/example/arm/ruby_fs.py +++ b/configs/example/arm/ruby_fs.py @@ -100,7 +100,7 @@ def create(args): """Create and configure the system object.""" if args.script and not os.path.isfile(args.script): - print("Error: Bootscript %s does not exist" % args.script) + print(f"Error: Bootscript {args.script} does not exist") sys.exit(1) cpu_class = cpu_types[args.cpu] @@ -115,7 +115,7 @@ def create(args): # Add CPU clusters to the system system.cpu_cluster = [ - devices.CpuCluster( + devices.ArmCpuCluster( system, args.num_cpus, args.cpu_freq, @@ -171,11 +171,11 @@ def create(args): # memory layout. "norandmaps", # Tell Linux where to find the root disk image. - "root=%s" % args.root_device, + f"root={args.root_device}", # Mount the root disk read-write by default. "rw", # Tell Linux about the amount of physical memory present. - "mem=%s" % args.mem_size, + f"mem={args.mem_size}", ] system.workload.command_line = " ".join(kernel_cmd) @@ -185,7 +185,7 @@ def create(args): def run(args): cptdir = m5.options.outdir if args.checkpoint: - print("Checkpoint directory: %s" % cptdir) + print(f"Checkpoint directory: {cptdir}") while True: event = m5.simulate() @@ -221,9 +221,7 @@ def main(): "--root-device", type=str, default=default_root_device, - help="OS device name for root partition (default: {})".format( - default_root_device - ), + help=f"OS device name for root partition (default: {default_root_device})", ) parser.add_argument( "--script", type=str, default="", help="Linux bootscript" diff --git a/configs/example/arm/starter_fs.py b/configs/example/arm/starter_fs.py index 3a9a8762d6..07280bd204 100644 --- a/configs/example/arm/starter_fs.py +++ b/configs/example/arm/starter_fs.py @@ -88,7 +88,7 @@ def create(args): """Create and configure the system object.""" if args.script and not os.path.isfile(args.script): - print("Error: Bootscript %s does not exist" % args.script) + print(f"Error: Bootscript {args.script} does not exist") sys.exit(1) cpu_class = cpu_types[args.cpu][0] @@ -128,8 +128,14 @@ def create(args): # Add CPU clusters to the system system.cpu_cluster = [ - devices.CpuCluster( - system, args.num_cores, args.cpu_freq, "1.0V", *cpu_types[args.cpu] + devices.ArmCpuCluster( + system, + args.num_cores, + args.cpu_freq, + "1.0V", + *cpu_types[args.cpu], + tarmac_gen=args.tarmac_gen, + tarmac_dest=args.tarmac_dest, ) ] @@ -163,21 +169,26 @@ def create(args): # memory layout. "norandmaps", # Tell Linux where to find the root disk image. - "root=%s" % args.root_device, + f"root={args.root_device}", # Mount the root disk read-write by default. "rw", # Tell Linux about the amount of physical memory present. - "mem=%s" % args.mem_size, + f"mem={args.mem_size}", ] system.workload.command_line = " ".join(kernel_cmd) + if args.with_pmu: + for cluster in system.cpu_cluster: + interrupt_numbers = [args.pmu_ppi_number] * len(cluster) + cluster.addPMUs(interrupt_numbers) + return system def run(args): cptdir = m5.options.outdir if args.checkpoint: - print("Checkpoint directory: %s" % cptdir) + print(f"Checkpoint directory: {cptdir}") while True: event = m5.simulate() @@ -188,10 +199,17 @@ def run(args): m5.checkpoint(os.path.join(cpt_dir)) print("Checkpoint done.") else: - print(exit_msg, " @ ", m5.curTick()) + print(f"{exit_msg} ({event.getCode()}) @ {m5.curTick()}") break - sys.exit(event.getCode()) + +def arm_ppi_arg(int_num: int) -> int: + """Argparse argument parser for valid Arm PPI numbers.""" + # PPIs (1056 <= int_num <= 1119) are not yet supported by gem5 + int_num = int(int_num) + if 16 <= int_num <= 31: + return int_num + raise ValueError(f"{int_num} is not a valid Arm PPI number") def main(): @@ -219,9 +237,7 @@ def main(): "--root-device", type=str, default=default_root_device, - help="OS device name for root partition (default: {})".format( - default_root_device - ), + help=f"OS device name for root partition (default: {default_root_device})", ) parser.add_argument( "--script", type=str, default="", help="Linux bootscript" @@ -259,6 +275,29 @@ def main(): default="2GB", help="Specify the physical memory size", ) + parser.add_argument( + "--tarmac-gen", + action="store_true", + help="Write a Tarmac trace.", + ) + parser.add_argument( + "--tarmac-dest", + choices=TarmacDump.vals, + default="stdoutput", + help="Destination for the Tarmac trace output. [Default: stdoutput]", + ) + parser.add_argument( + "--with-pmu", + action="store_true", + help="Add a PMU to each core in the cluster.", + ) + parser.add_argument( + "--pmu-ppi-number", + type=arm_ppi_arg, + default=23, + help="The number of the PPI to use to connect each PMU to its core. " + "Must be an integer and a valid PPI number (16 <= int_num <= 31).", + ) parser.add_argument("--checkpoint", action="store_true") parser.add_argument("--restore", type=str, default=None) diff --git a/configs/example/arm/starter_se.py b/configs/example/arm/starter_se.py index 08c3d74fbd..f21f399675 100644 --- a/configs/example/arm/starter_se.py +++ b/configs/example/arm/starter_se.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2017 ARM Limited +# Copyright (c) 2016-2017, 2022-2023 Arm Limited # All rights reserved. # # The license below extends only to copyright in the software and shall @@ -95,30 +95,36 @@ class SimpleSeSystem(System): # Add CPUs to the system. A cluster of CPUs typically have # private L1 caches and a shared L2 cache. - self.cpu_cluster = devices.CpuCluster( - self, args.num_cores, args.cpu_freq, "1.2V", *cpu_types[args.cpu] + self.cpu_cluster = devices.ArmCpuCluster( + self, + args.num_cores, + args.cpu_freq, + "1.2V", + *cpu_types[args.cpu], + tarmac_gen=args.tarmac_gen, + tarmac_dest=args.tarmac_dest, ) # Create a cache hierarchy (unless we are simulating a # functional CPU in atomic memory mode) for the CPU cluster # and connect it to the shared memory bus. - if self.cpu_cluster.memoryMode() == "timing": + if self.cpu_cluster.memory_mode() == "timing": self.cpu_cluster.addL1() self.cpu_cluster.addL2(self.cpu_cluster.clk_domain) self.cpu_cluster.connectMemSide(self.membus) # Tell gem5 about the memory mode used by the CPUs we are # simulating. - self.mem_mode = self.cpu_cluster.memoryMode() + self.mem_mode = self.cpu_cluster.memory_mode() def numCpuClusters(self): return len(self._clusters) - def addCpuCluster(self, cpu_cluster, num_cpus): + def addCpuCluster(self, cpu_cluster): assert cpu_cluster not in self._clusters - assert num_cpus > 0 + assert len(cpu_cluster) > 0 self._clusters.append(cpu_cluster) - self._num_cpus += num_cpus + self._num_cpus += len(cpu_cluster) def numCpus(self): return self._num_cpus @@ -215,6 +221,17 @@ def main(): default="2GB", help="Specify the physical memory size", ) + parser.add_argument( + "--tarmac-gen", + action="store_true", + help="Write a Tarmac trace.", + ) + parser.add_argument( + "--tarmac-dest", + choices=TarmacDump.vals, + default="stdoutput", + help="Destination for the Tarmac trace output. [Default: stdoutput]", + ) args = parser.parse_args() @@ -240,8 +257,7 @@ def main(): # Print the reason for the simulation exit. Some exit codes are # requests for service (e.g., checkpoints) from the simulation # script. We'll just ignore them here and exit. - print(event.getCause(), " @ ", m5.curTick()) - sys.exit(event.getCode()) + print(f"{event.getCause()} ({event.getCode()}) @ {m5.curTick()}") if __name__ == "__m5_main__": diff --git a/configs/example/dramsys.py b/configs/example/dramsys.py new file mode 100755 index 0000000000..934ff17b57 --- /dev/null +++ b/configs/example/dramsys.py @@ -0,0 +1,63 @@ +# 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: 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 m5 + +from m5.objects import * + +traffic_gen = PyTrafficGen() +system = System() +vd = VoltageDomain(voltage="1V") + +system.mem_mode = "timing" + +system.cpu = traffic_gen + +dramsys = DRAMSys( + configuration="ext/dramsys/DRAMSys/DRAMSys/" + "library/resources/simulations/ddr4-example.json", + resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", +) + +system.target = dramsys +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() +idle = traffic_gen.createIdle(100000) +linear = traffic_gen.createLinear(10000000, 0, 16777216, 64, 500, 1500, 65, 0) +random = traffic_gen.createRandom(10000000, 0, 16777216, 64, 500, 1500, 65, 0) +traffic_gen.start([linear, idle, random]) + +cause = m5.simulate(20000000).getCause() +print(cause) diff --git a/configs/example/fs.py b/configs/example/fs.py index 0e31cfccac..30b4f19553 100644 --- a/configs/example/fs.py +++ b/configs/example/fs.py @@ -1,19 +1,4 @@ -# Copyright (c) 2010-2013, 2016, 2019-2020 ARM Limited -# Copyright (c) 2020 Barkhausen Institut -# 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-2014 Mark D. Hill and David A. Wood -# Copyright (c) 2009-2011 Advanced Micro Devices, Inc. -# Copyright (c) 2006-2007 The Regents of The University of Michigan +# Copyright (c) 2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -39,401 +24,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 argparse -import sys +from m5.util import fatal -import m5 -from m5.defines import buildEnv -from m5.objects import * -from m5.util import addToPath, fatal, warn -from m5.util.fdthelper import * -from gem5.isas import ISA -from gem5.runtime import get_runtime_isa - -addToPath("../") - -from ruby import Ruby - -from common.FSConfig import * -from common.SysPaths import * -from common.Benchmarks import * -from common import Simulation -from common import CacheConfig -from common import CpuConfig -from common import MemConfig -from common import ObjectList -from common.Caches import * -from common import Options - - -def cmd_line_template(): - if args.command_line and args.command_line_file: - print( - "Error: --command-line and --command-line-file are " - "mutually exclusive" - ) - sys.exit(1) - if args.command_line: - return args.command_line - if args.command_line_file: - return open(args.command_line_file).read().strip() - return None - - -def build_test_system(np): - cmdline = cmd_line_template() - isa = get_runtime_isa() - if isa == ISA.MIPS: - test_sys = makeLinuxMipsSystem(test_mem_mode, bm[0], cmdline=cmdline) - elif isa == ISA.SPARC: - test_sys = makeSparcSystem(test_mem_mode, bm[0], cmdline=cmdline) - elif isa == ISA.RISCV: - test_sys = makeBareMetalRiscvSystem( - test_mem_mode, bm[0], cmdline=cmdline - ) - elif isa == ISA.X86: - test_sys = makeLinuxX86System( - test_mem_mode, np, bm[0], args.ruby, cmdline=cmdline - ) - elif isa == ISA.ARM: - test_sys = makeArmSystem( - test_mem_mode, - args.machine_type, - np, - bm[0], - args.dtb_filename, - bare_metal=args.bare_metal, - cmdline=cmdline, - external_memory=args.external_memory_system, - ruby=args.ruby, - vio_9p=args.vio_9p, - bootloader=args.bootloader, - ) - if args.enable_context_switch_stats_dump: - test_sys.enable_context_switch_stats_dump = True - else: - fatal("Incapable of building %s full system!", isa.name) - - # Set the cache line size for the entire system - test_sys.cache_line_size = args.cacheline_size - - # Create a top-level voltage domain - test_sys.voltage_domain = VoltageDomain(voltage=args.sys_voltage) - - # Create a source clock for the system and set the clock period - test_sys.clk_domain = SrcClockDomain( - clock=args.sys_clock, voltage_domain=test_sys.voltage_domain - ) - - # Create a CPU voltage domain - test_sys.cpu_voltage_domain = VoltageDomain() - - # Create a source clock for the CPUs and set the clock period - test_sys.cpu_clk_domain = SrcClockDomain( - clock=args.cpu_clock, voltage_domain=test_sys.cpu_voltage_domain - ) - - if buildEnv["USE_RISCV_ISA"]: - test_sys.workload.bootloader = args.kernel - elif args.kernel is not None: - test_sys.workload.object_file = binary(args.kernel) - - if args.script is not None: - test_sys.readfile = args.script - - test_sys.init_param = args.init_param - - # For now, assign all the CPUs to the same clock domain - test_sys.cpu = [ - TestCPUClass(clk_domain=test_sys.cpu_clk_domain, cpu_id=i) - for i in range(np) - ] - - if args.ruby: - bootmem = getattr(test_sys, "_bootmem", None) - Ruby.create_system( - args, True, test_sys, test_sys.iobus, test_sys._dma_ports, bootmem - ) - - # Create a seperate clock domain for Ruby - test_sys.ruby.clk_domain = SrcClockDomain( - clock=args.ruby_clock, voltage_domain=test_sys.voltage_domain - ) - - # Connect the ruby io port to the PIO bus, - # assuming that there is just one such port. - test_sys.iobus.mem_side_ports = test_sys.ruby._io_port.in_ports - - for (i, cpu) in enumerate(test_sys.cpu): - # - # Tie the cpu ports to the correct ruby system ports - # - cpu.clk_domain = test_sys.cpu_clk_domain - cpu.createThreads() - cpu.createInterruptController() - - test_sys.ruby._cpu_ports[i].connectCpuPorts(cpu) - - else: - if args.caches or args.l2cache: - # By default the IOCache runs at the system clock - test_sys.iocache = IOCache(addr_ranges=test_sys.mem_ranges) - test_sys.iocache.cpu_side = test_sys.iobus.mem_side_ports - test_sys.iocache.mem_side = test_sys.membus.cpu_side_ports - elif not args.external_memory_system: - test_sys.iobridge = Bridge( - delay="50ns", ranges=test_sys.mem_ranges - ) - test_sys.iobridge.cpu_side_port = test_sys.iobus.mem_side_ports - test_sys.iobridge.mem_side_port = test_sys.membus.cpu_side_ports - - # Sanity check - if args.simpoint_profile: - if not ObjectList.is_noncaching_cpu(TestCPUClass): - fatal("SimPoint generation should be done with atomic cpu") - if np > 1: - fatal( - "SimPoint generation not supported with more than one CPUs" - ) - - for i in range(np): - if args.simpoint_profile: - test_sys.cpu[i].addSimPointProbe(args.simpoint_interval) - if args.checker: - test_sys.cpu[i].addCheckerCpu() - if not ObjectList.is_kvm_cpu(TestCPUClass): - if args.bp_type: - bpClass = ObjectList.bp_list.get(args.bp_type) - test_sys.cpu[i].branchPred = bpClass() - if args.indirect_bp_type: - IndirectBPClass = ObjectList.indirect_bp_list.get( - args.indirect_bp_type - ) - test_sys.cpu[ - i - ].branchPred.indirectBranchPred = IndirectBPClass() - test_sys.cpu[i].createThreads() - - # If elastic tracing is enabled when not restoring from checkpoint and - # when not fast forwarding using the atomic cpu, then check that the - # TestCPUClass is DerivO3CPU or inherits from DerivO3CPU. If the check - # passes then attach the elastic trace probe. - # If restoring from checkpoint or fast forwarding, the code that does this for - # FutureCPUClass is in the Simulation module. If the check passes then the - # elastic trace probe is attached to the switch CPUs. - if ( - args.elastic_trace_en - and args.checkpoint_restore == None - and not args.fast_forward - ): - CpuConfig.config_etrace(TestCPUClass, test_sys.cpu, args) - - CacheConfig.config_cache(args, test_sys) - - MemConfig.config_mem(args, test_sys) - - if ObjectList.is_kvm_cpu(TestCPUClass) or ObjectList.is_kvm_cpu( - FutureClass - ): - # Assign KVM CPUs to their own event queues / threads. This - # has to be done after creating caches and other child objects - # since these mustn't inherit the CPU event queue. - for i, cpu in enumerate(test_sys.cpu): - # Child objects usually inherit the parent's event - # queue. Override that and use the same event queue for - # all devices. - for obj in cpu.descendants(): - obj.eventq_index = 0 - cpu.eventq_index = i + 1 - test_sys.kvm_vm = KvmVM() - - return test_sys - - -def build_drive_system(np): - # driver system CPU is always simple, so is the memory - # Note this is an assignment of a class, not an instance. - DriveCPUClass = AtomicSimpleCPU - drive_mem_mode = "atomic" - DriveMemClass = SimpleMemory - - cmdline = cmd_line_template() - if buildEnv["USE_MIPS_ISA"]: - drive_sys = makeLinuxMipsSystem(drive_mem_mode, bm[1], cmdline=cmdline) - elif buildEnv["USE_SPARC_ISA"]: - drive_sys = makeSparcSystem(drive_mem_mode, bm[1], cmdline=cmdline) - elif buildEnv["USE_X86_ISA"]: - drive_sys = makeLinuxX86System( - drive_mem_mode, np, bm[1], cmdline=cmdline - ) - elif buildEnv["USE_ARM_ISA"]: - drive_sys = makeArmSystem( - drive_mem_mode, - args.machine_type, - np, - bm[1], - args.dtb_filename, - cmdline=cmdline, - ) - - # Create a top-level voltage domain - drive_sys.voltage_domain = VoltageDomain(voltage=args.sys_voltage) - - # Create a source clock for the system and set the clock period - drive_sys.clk_domain = SrcClockDomain( - clock=args.sys_clock, voltage_domain=drive_sys.voltage_domain - ) - - # Create a CPU voltage domain - drive_sys.cpu_voltage_domain = VoltageDomain() - - # Create a source clock for the CPUs and set the clock period - drive_sys.cpu_clk_domain = SrcClockDomain( - clock=args.cpu_clock, voltage_domain=drive_sys.cpu_voltage_domain - ) - - drive_sys.cpu = DriveCPUClass( - clk_domain=drive_sys.cpu_clk_domain, cpu_id=0 - ) - drive_sys.cpu.createThreads() - drive_sys.cpu.createInterruptController() - drive_sys.cpu.connectBus(drive_sys.membus) - if args.kernel is not None: - drive_sys.workload.object_file = binary(args.kernel) - - if ObjectList.is_kvm_cpu(DriveCPUClass): - drive_sys.kvm_vm = KvmVM() - - drive_sys.iobridge = Bridge(delay="50ns", ranges=drive_sys.mem_ranges) - drive_sys.iobridge.cpu_side_port = drive_sys.iobus.mem_side_ports - drive_sys.iobridge.mem_side_port = drive_sys.membus.cpu_side_ports - - # Create the appropriate memory controllers and connect them to the - # memory bus - drive_sys.mem_ctrls = [ - DriveMemClass(range=r) for r in drive_sys.mem_ranges - ] - for i in range(len(drive_sys.mem_ctrls)): - drive_sys.mem_ctrls[i].port = drive_sys.membus.mem_side_ports - - drive_sys.init_param = args.init_param - - return drive_sys - - -# Add args -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) -Options.addFSOptions(parser) - -# Add the ruby specific and protocol specific args -if "--ruby" in sys.argv: - Ruby.define_options(parser) - -args = parser.parse_args() - -# system under test can be any CPU -(TestCPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args) - -# Match the memories with the CPUs, based on the options for the test system -TestMemClass = Simulation.setMemClass(args) - -if args.benchmark: - try: - bm = Benchmarks[args.benchmark] - except KeyError: - print("Error benchmark %s has not been defined." % args.benchmark) - print("Valid benchmarks are: %s" % DefinedBenchmarks) - sys.exit(1) -else: - if args.dual: - bm = [ - SysConfig( - disks=args.disk_image, - rootdev=args.root_device, - mem=args.mem_size, - os_type=args.os_type, - ), - SysConfig( - disks=args.disk_image, - rootdev=args.root_device, - mem=args.mem_size, - os_type=args.os_type, - ), - ] - else: - bm = [ - SysConfig( - disks=args.disk_image, - rootdev=args.root_device, - mem=args.mem_size, - os_type=args.os_type, - ) - ] - -np = args.num_cpus - -test_sys = build_test_system(np) - -if len(bm) == 2: - drive_sys = build_drive_system(np) - root = makeDualRoot(True, test_sys, drive_sys, args.etherdump) -elif len(bm) == 1 and args.dist: - # This system is part of a dist-gem5 simulation - root = makeDistRoot( - test_sys, - args.dist_rank, - args.dist_size, - args.dist_server_name, - args.dist_server_port, - args.dist_sync_repeat, - args.dist_sync_start, - args.ethernet_linkspeed, - args.ethernet_linkdelay, - args.etherdump, - ) -elif len(bm) == 1: - root = Root(full_system=True, system=test_sys) -else: - print("Error I don't know how to create more than 2 systems.") - sys.exit(1) - -if ObjectList.is_kvm_cpu(TestCPUClass) or ObjectList.is_kvm_cpu(FutureClass): - # Required for running kvm on multiple host cores. - # Uses gem5's parallel event queue feature - # Note: The simulator is quite picky about this number! - root.sim_quantum = int(1e9) # 1 ms - -if args.timesync: - root.time_sync_enable = True - -if args.frame_capture: - VncServer.frame_capture = True - -if buildEnv["USE_ARM_ISA"] and not args.bare_metal and not args.dtb_filename: - if args.machine_type not in [ - "VExpress_GEM5", - "VExpress_GEM5_V1", - "VExpress_GEM5_V2", - "VExpress_GEM5_Foundation", - ]: - warn( - "Can only correctly generate a dtb for VExpress_GEM5_* " - "platforms, unless custom hardware models have been equipped " - "with generation functionality." - ) - - # Generate a Device Tree - for sysname in ("system", "testsys", "drivesys"): - if hasattr(root, sysname): - sys = getattr(root, sysname) - sys.workload.dtb_filename = os.path.join( - m5.options.outdir, "%s.dtb" % sysname - ) - sys.generateDtb(sys.workload.dtb_filename) - -if args.wait_gdb: - test_sys.workload.wait_for_remote_gdb = True - -Simulation.setWorkCountOptions(test_sys, args) -Simulation.run(args, root, test_sys, FutureClass) +fatal( + "The 'configs/example/fs.py' script has been deprecated. It can be " + "found in 'configs/deprecated/example' if required. Its usage should be " + "avoided as it will be removed in future releases of gem5." +) diff --git a/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py b/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py index e112b76ddb..60a7dd0f59 100644 --- a/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py +++ b/configs/example/gem5_library/checkpoints/riscv-hello-restore-checkpoint.py @@ -90,7 +90,7 @@ board = SimpleBoard( board.set_se_binary_workload( # the workload should be the same as the save-checkpoint script Resource("riscv-hello"), - checkpoint=Resource("riscv-hello-example-checkpoint-v22-1"), + checkpoint=Resource("riscv-hello-example-checkpoint-v23"), ) simulator = Simulator( diff --git a/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py b/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py index d2d1af730f..b5eb7e9912 100644 --- a/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py +++ b/configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py @@ -58,6 +58,7 @@ from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA from gem5.resources.workload import Workload +from gem5.resources.resource import obtain_resource, SimpointResource from pathlib import Path from gem5.components.cachehierarchies.classic.no_cache import NoCache from gem5.simulate.exit_event_generators import ( @@ -108,7 +109,23 @@ board = SimpleBoard( cache_hierarchy=cache_hierarchy, ) -board.set_workload(Workload("x86-print-this-15000-with-simpoints")) +# board.set_workload( +# Workload("x86-print-this-15000-with-simpoints") +# +# **Note: This has been removed until we update the resources.json file to +# encapsulate the new Simpoint format. +# Below we set the simpount manually. + +board.set_se_simpoint_workload( + binary=obtain_resource("x86-print-this"), + arguments=["print this", 15000], + simpoint=SimpointResource( + simpoint_interval=1000000, + simpoint_list=[2, 3, 4, 15], + weight_list=[0.1, 0.2, 0.4, 0.3], + warmup_interval=1000000, + ), +) dir = Path(args.checkpoint_path) dir.mkdir(exist_ok=True) diff --git a/configs/example/gem5_library/checkpoints/simpoints-se-restore.py b/configs/example/gem5_library/checkpoints/simpoints-se-restore.py index f8f48d0ec1..d063c143a7 100644 --- a/configs/example/gem5_library/checkpoints/simpoints-se-restore.py +++ b/configs/example/gem5_library/checkpoints/simpoints-se-restore.py @@ -63,8 +63,9 @@ from gem5.components.memory import DualChannelDDR4_2400 from gem5.components.processors.simple_processor import SimpleProcessor from gem5.components.processors.cpu_types import CPUTypes from gem5.isas import ISA -from gem5.resources.resource import Resource +from gem5.resources.resource import SimpointResource, obtain_resource from gem5.resources.workload import Workload +from gem5.resources.resource import SimpointResource from pathlib import Path from m5.stats import reset, dump @@ -96,11 +97,29 @@ board = SimpleBoard( cache_hierarchy=cache_hierarchy, ) -# Here we obtain the workloadfrom gem5 resources, the checkpoint in this +# Here we obtain the workload from gem5 resources, the checkpoint in this # workload was generated from # `configs/example/gem5_library/checkpoints/simpoints-se-checkpoint.py`. -board.set_workload( - Workload("x86-print-this-15000-with-simpoints-and-checkpoint") +# board.set_workload( +# Workload("x86-print-this-15000-with-simpoints-and-checkpoint") +# +# **Note: This has been removed until we update the resources.json file to +# encapsulate the new Simpoint format. +# Below we set the simpount manually. +# +# This loads a single checkpoint as an example of using simpoints to simulate +# the function of a single simpoint region. + +board.set_se_simpoint_workload( + binary=obtain_resource("x86-print-this"), + arguments=["print this", 15000], + simpoint=SimpointResource( + simpoint_interval=1000000, + simpoint_list=[2, 3, 4, 15], + weight_list=[0.1, 0.2, 0.4, 0.3], + warmup_interval=1000000, + ), + checkpoint=obtain_resource("simpoints-se-checkpoints-v23-0-v1"), ) diff --git a/configs/example/gem5_library/dramsys/arm-hello-dramsys.py b/configs/example/gem5_library/dramsys/arm-hello-dramsys.py new file mode 100644 index 0000000000..8b25a36396 --- /dev/null +++ b/configs/example/gem5_library/dramsys/arm-hello-dramsys.py @@ -0,0 +1,92 @@ +# 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 +# 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 an ARM +"hello world" binary using the DRAMSys simulator. + +**Important Note**: DRAMSys must be compiled into the gem5 binary to use the +DRRAMSys simulator. Please consult 'ext/dramsys/README' on how to compile +correctly. If this is not done correctly this script will run with error. +""" + +from gem5.isas import ISA +from gem5.utils.requires import requires +from gem5.resources.resource import Resource +from gem5.components.memory import DRAMSysDDR3_1600 +from gem5.components.processors.cpu_types import CPUTypes +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.cachehierarchies.classic.private_l1_cache_hierarchy import ( + PrivateL1CacheHierarchy, +) +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.simulate.simulator import Simulator + +# This check ensures the gem5 binary is compiled to the ARM ISA target. If not, +# an exception will be thrown. +requires(isa_required=ISA.ARM) + +# We need a cache as DRAMSys only accepts requests with the size of a cache line +cache_hierarchy = PrivateL1CacheHierarchy(l1d_size="32kB", l1i_size="32kB") + +# We use a single channel DDR3_1600 memory system +memory = DRAMSysDDR3_1600(recordable=True) + +# We use a simple Timing processor with one core. +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. +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 ARM 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("arm-hello64-static") +) + +# Lastly we run the simulation. +simulator = Simulator(board=board) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), simulator.get_last_exit_event_cause() + ) +) diff --git a/configs/example/gem5_library/dramsys/dramsys-traffic.py b/configs/example/gem5_library/dramsys/dramsys-traffic.py new file mode 100644 index 0000000000..ee9ad7228d --- /dev/null +++ b/configs/example/gem5_library/dramsys/dramsys-traffic.py @@ -0,0 +1,62 @@ +# Copyright (c) 2023 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 is used for running a traffic generator connected to the +DRAMSys simulator. + +**Important Note**: DRAMSys must be compiled into the gem5 binary to use the +DRRAMSys simulator. Please consult 'ext/dramsys/README' on how to compile +correctly. If this is not done correctly this script will run with error. +""" +import m5 +from gem5.components.memory import DRAMSysMem +from gem5.components.boards.test_board import TestBoard +from gem5.components.processors.linear_generator import LinearGenerator +from m5.objects import Root + +memory = DRAMSysMem( + configuration="ext/dramsys/DRAMSys/DRAMSys/" + "library/resources/simulations/ddr4-example.json", + resource_directory="ext/dramsys/DRAMSys/DRAMSys/library/resources", + recordable=True, + size="4GB", +) + +generator = LinearGenerator( + duration="250us", + rate="40GB/s", + num_cores=1, + max_addr=memory.get_size(), +) +board = TestBoard( + clk_freq="3GHz", generator=generator, memory=memory, cache_hierarchy=None +) + +root = Root(full_system=False, system=board) +board._pre_instantiate() +m5.instantiate() +generator.start_traffic() +exit_event = m5.simulate() diff --git a/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py b/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py new file mode 100644 index 0000000000..abb15fb7f8 --- /dev/null +++ b/configs/example/gem5_library/looppoints/create-looppoint-checkpoints.py @@ -0,0 +1,138 @@ +# Copyright (c) 2023 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 configuration script shows an example of how to take checkpoints for +LoopPoint using the gem5 stdlib. To take checkpoints for LoopPoint simulation +regions, there must be a LoopPoint data file generated by Pin or the gem5 +simulator. With the information in the LoopPoint data file, the stdlib +modules will take checkpoints at the beginning of the simulation regions +(warmup region included if it exists) and record all restore needed information +into a JSON file. The JSON file is needed for later restoring, so please call +`looppoint.output_json_file()` at the end of the simulation. + +This script builds a simple board with the gem5 stdlib with no cache and a +simple memory structure to take checkpoints. Some of the components, such as +cache hierarchy, can be changed when restoring checkpoints. + +Usage +----- +``` +scons build/X86/gem5.opt +./build/X86/gem5.opt \ + configs/example/gem5_library/looppoints/create-looppoint-checkpoint.py +``` +""" + +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires +from gem5.components.cachehierarchies.classic.no_cache import NoCache +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.memory.single_channel import SingleChannelDDR3_1600 +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.components.processors.cpu_types import CPUTypes +from gem5.isas import ISA +from gem5.resources.workload import Workload +from pathlib import Path +from gem5.simulate.exit_event_generators import ( + looppoint_save_checkpoint_generator, +) + +import argparse + +requires(isa_required=ISA.X86) + +parser = argparse.ArgumentParser( + description="An example looppoint workload file path" +) + +# The lone arguments is a file path to a directory to store the checkpoints. + +parser.add_argument( + "--checkpoint-path", + type=str, + required=False, + default="looppoint_checkpoints_folder", + help="The directory to store the checkpoints.", +) + +args = parser.parse_args() + +# When taking a checkpoint, the cache state is not saved, so the cache +# hierarchy can be changed completely when restoring from a checkpoint. +# By using NoCache() to take checkpoints, it can slightly improve the +# performance when running in atomic mode, and it will not put any restrictions +# on what people can do with the checkpoints. +cache_hierarchy = NoCache() + + +# Using simple memory to take checkpoints might slightly imporve the +# performance in atomic mode. The memory structure can be changed when +# restoring from a checkpoint, but the size of the memory must be equal or +# greater to that taken when creating the checkpoint. +memory = SingleChannelDDR3_1600(size="2GB") + +processor = SimpleProcessor( + cpu_type=CPUTypes.ATOMIC, + isa=ISA.X86, + # LoopPoint can work with multicore workloads + num_cores=9, +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +board.set_workload(Workload("x86-matrix-multiply-omp-100-8-looppoint-csv")) + +dir = Path(args.checkpoint_path) +dir.mkdir(exist_ok=True) + +simulator = Simulator( + board=board, + on_exit_event={ + ExitEvent.SIMPOINT_BEGIN: looppoint_save_checkpoint_generator( + checkpoint_dir=dir, + looppoint=board.get_looppoint(), + # True if the relative PC count pairs should be updated during the + # simulation. Default as True. + update_relatives=True, + # True if the simulation loop should exit after all the PC count + # pairs in the LoopPoint data file have been encountered. Default + # as True. + exit_when_empty=True, + ) + }, +) + +simulator.run() + +# Output the JSON file +board.get_looppoint().output_json_file() diff --git a/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py b/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py new file mode 100644 index 0000000000..21353a34a1 --- /dev/null +++ b/configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py @@ -0,0 +1,139 @@ +# Copyright (c) 2023 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 configuration script shows an example of how to restore a checkpoint that +was taken for a LoopPoint simulation region in the example-restore.py. +All the LoopPoint information should be passed in through the JSON file +generated by the gem5 simulator when all the checkpoints were taken. + +This script builds a more complex board than the board used for taking +checkpoints. + +Usage +----- +``` +./build/X86/gem5.opt \ + configs/example/gem5_library/looppoints/restore-looppoint-checkpoint.py +``` +""" +import argparse + +from gem5.simulate.exit_event import ExitEvent +from gem5.simulate.simulator import Simulator +from gem5.utils.requires import requires +from gem5.components.cachehierarchies.classic.private_l1_private_l2_cache_hierarchy import ( + PrivateL1PrivateL2CacheHierarchy, +) +from gem5.components.boards.simple_board import SimpleBoard +from gem5.components.memory import DualChannelDDR4_2400 +from gem5.components.processors.simple_processor import SimpleProcessor +from gem5.components.processors.cpu_types import CPUTypes +from gem5.isas import ISA +from gem5.resources.resource import obtain_resource +from gem5.resources.workload import Workload +from m5.stats import reset, dump + +requires(isa_required=ISA.X86) + +parser = argparse.ArgumentParser(description="An restore checkpoint script.") + +parser.add_argument( + "--checkpoint-region", + type=str, + required=False, + choices=( + "1", + "2", + "3", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + ), + default="1", + help="The checkpoint region to restore from.", +) +args = parser.parse_args() + +# The cache hierarchy can be different from the cache hierarchy used in taking +# the checkpoints +cache_hierarchy = PrivateL1PrivateL2CacheHierarchy( + l1d_size="32kB", + l1i_size="32kB", + l2_size="256kB", +) + +# The memory structure can be different from the memory structure used in +# taking the checkpoints, but the size of the memory must be equal or larger. +memory = DualChannelDDR4_2400(size="2GB") + +processor = SimpleProcessor( + cpu_type=CPUTypes.TIMING, + isa=ISA.X86, + # The number of cores must be equal or greater than that used when taking + # the checkpoint. + num_cores=9, +) + +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +board.set_workload( + Workload( + f"x86-matrix-multiply-omp-100-8-looppoint-region-{args.checkpoint_region}" + ) +) + +# This generator will dump the stats and exit the simulation loop when the +# simulation region reaches its end. In the case there is a warmup interval, +# the simulation stats are reset after the warmup is complete. +def reset_and_dump(): + if len(board.get_looppoint().get_targets()) > 1: + print("Warmup region ended. Resetting stats.") + reset() + yield False + print("Region ended. Dumping stats.") + dump() + yield True + + +simulator = Simulator( + board=board, + on_exit_event={ExitEvent.SIMPOINT_BEGIN: reset_and_dump()}, +) + +simulator.run() diff --git a/configs/example/gem5_library/power-hello.py b/configs/example/gem5_library/power-hello.py new file mode 100644 index 0000000000..cf31778945 --- /dev/null +++ b/configs/example/gem5_library/power-hello.py @@ -0,0 +1,89 @@ +# Copyright (c) 2023 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 a POWER +"hello world" binary. + +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/POWER/gem5.opt +./build/POWER/gem5.opt configs/example/gem5_library/power-hello.py +``` +""" + +from gem5.isas import ISA +from gem5.utils.requires import requires +from gem5.resources.resource import Resource +from gem5.components.memory import SingleChannelDDR4_2400 +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 POWER ISA target. +# If not, an exception will be thrown. +requires(isa_required=ISA.POWER) + +# In this setup we don't have a cache. `NoCache` can be used for such setups. +cache_hierarchy = NoCache() + +# We use a single channel DDR4_2400 memory system +memory = SingleChannelDDR4_2400(size="32MB") + +# We use a simple ATOMIC processor with one core. +processor = SimpleProcessor( + cpu_type=CPUTypes.ATOMIC, isa=ISA.POWER, num_cores=1 +) + +# The gem5 library simple board which can be used to run simple SE-mode +# simulations. +board = SimpleBoard( + clk_freq="3GHz", + processor=processor, + memory=memory, + cache_hierarchy=cache_hierarchy, +) + +board.set_se_binary_workload(Resource("power-hello")) + +# Lastly we run the simulation. +simulator = Simulator(board=board) +simulator.run() + +print( + "Exiting @ tick {} because {}.".format( + simulator.get_current_tick(), + simulator.get_last_exit_event_cause(), + ) +) diff --git a/configs/example/gem5_library/riscvmatched-hello.py b/configs/example/gem5_library/riscvmatched-hello.py index d8ae8e5f9c..e7b4cf7128 100644 --- a/configs/example/gem5_library/riscvmatched-hello.py +++ b/configs/example/gem5_library/riscvmatched-hello.py @@ -39,9 +39,7 @@ scons build/RISCV/gem5.opt from gem5.resources.resource import Resource from gem5.simulate.simulator import Simulator -from python.gem5.prebuilt.riscvmatched.riscvmatched_board import ( - RISCVMatchedBoard, -) +from gem5.prebuilt.riscvmatched.riscvmatched_board import RISCVMatchedBoard from gem5.isas import ISA from gem5.utils.requires import requires diff --git a/configs/example/gem5_library/x86-gapbs-benchmarks.py b/configs/example/gem5_library/x86-gapbs-benchmarks.py index 6ab37479f9..b85ce6e7e8 100644 --- a/configs/example/gem5_library/x86-gapbs-benchmarks.py +++ b/configs/example/gem5_library/x86-gapbs-benchmarks.py @@ -195,9 +195,9 @@ if args.synthetic == "1": ) exit(-1) - command = "./{} -g {}\n".format(args.benchmark, args.size) + command = f"./{args.benchmark} -g {args.size}\n" else: - command = "./{} -sf ../{}".format(args.benchmark, args.size) + command = f"./{args.benchmark} -sf ../{args.size}" board.set_kernel_disk_workload( # The x86 linux kernel will be automatically downloaded to the @@ -262,7 +262,9 @@ print("Done with the simulation") print() print("Performance statistics:") -print("Simulated time in ROI: %.2fs" % ((end_tick - start_tick) / 1e12)) +print( + f"Simulated time in ROI: {(end_tick - start_tick) / 1000000000000.0:.2f}s" +) print( "Ran a total of", simulator.get_current_tick() / 1e12, "simulated seconds" ) diff --git a/configs/example/gem5_library/x86-npb-benchmarks.py b/configs/example/gem5_library/x86-npb-benchmarks.py index ff363e449c..cffba5a294 100644 --- a/configs/example/gem5_library/x86-npb-benchmarks.py +++ b/configs/example/gem5_library/x86-npb-benchmarks.py @@ -195,7 +195,7 @@ board = X86Board( # properly. command = ( - "/home/gem5/NPB3.3-OMP/bin/{}.{}.x;".format(args.benchmark, args.size) + f"/home/gem5/NPB3.3-OMP/bin/{args.benchmark}.{args.size}.x;" + "sleep 5;" + "m5 exit;" ) diff --git a/configs/example/gem5_library/x86-parsec-benchmarks.py b/configs/example/gem5_library/x86-parsec-benchmarks.py index 190c0a0980..aaffec8edc 100644 --- a/configs/example/gem5_library/x86-parsec-benchmarks.py +++ b/configs/example/gem5_library/x86-parsec-benchmarks.py @@ -177,10 +177,7 @@ board = X86Board( command = ( "cd /home/gem5/parsec-benchmark;".format(args.benchmark) + "source env.sh;" - + "parsecmgmt -a run -p {} -c gcc-hooks -i {} \ - -n {};".format( - args.benchmark, args.size, "2" - ) + + f"parsecmgmt -a run -p {args.benchmark} -c gcc-hooks -i {args.size} -n 2;" + "sleep 5;" + "m5 exit;" ) diff --git a/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py b/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py index 8f39f49e2e..10d5da0adb 100644 --- a/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py +++ b/configs/example/gem5_library/x86-spec-cpu2006-benchmarks.py @@ -179,7 +179,7 @@ if not os.path.exists(args.image): print( "https://gem5art.readthedocs.io/en/latest/tutorials/spec-tutorial.html" ) - fatal("The disk-image is not found at {}".format(args.image)) + fatal(f"The disk-image is not found at {args.image}") # Setting up all the fixed system parameters here # Caches: MESI Two Level Cache Hierarchy @@ -252,7 +252,7 @@ except FileExistsError: # The runscript.sh file places `m5 exit` before and after the following command # Therefore, we only pass this command without m5 exit. -command = "{} {} {}".format(args.benchmark, args.size, output_dir) +command = f"{args.benchmark} {args.size} {output_dir}" board.set_kernel_disk_workload( # The x86 linux kernel will be automatically downloaded to the @@ -262,7 +262,7 @@ board.set_kernel_disk_workload( kernel=Resource("x86-linux-kernel-4.19.83"), # The location of the x86 SPEC CPU 2017 image disk_image=CustomDiskImageResource( - args.image, disk_root_partition=args.partition + args.image, root_partition=args.partition ), readfile_contents=command, ) @@ -272,6 +272,7 @@ def handle_exit(): print("Done bootling Linux") print("Resetting stats at the start of ROI!") m5.stats.reset() + processor.switch() yield False # E.g., continue the simulation. print("Dump stats at the end of the ROI!") m5.stats.dump() @@ -304,7 +305,11 @@ print("All simulation events were successful.") print("Performance statistics:") -print("Simulated time: " + ((str(simulator.get_roi_ticks()[0])))) +roi_begin_ticks = simulator.get_tick_stopwatch()[0][1] +roi_end_ticks = simulator.get_tick_stopwatch()[1][1] + +print("roi simulated ticks: " + str(roi_end_ticks - roi_begin_ticks)) + print( "Ran a total of", simulator.get_current_tick() / 1e12, "simulated seconds" ) diff --git a/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py b/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py index c4af7f5dd9..cb5f5d19e3 100644 --- a/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py +++ b/configs/example/gem5_library/x86-spec-cpu2017-benchmarks.py @@ -193,7 +193,7 @@ if not os.path.exists(args.image): print( "https://gem5art.readthedocs.io/en/latest/tutorials/spec-tutorial.html" ) - fatal("The disk-image is not found at {}".format(args.image)) + fatal(f"The disk-image is not found at {args.image}") # Setting up all the fixed system parameters here # Caches: MESI Two Level Cache Hierarchy @@ -266,7 +266,7 @@ except FileExistsError: # The runscript.sh file places `m5 exit` before and after the following command # Therefore, we only pass this command without m5 exit. -command = "{} {} {}".format(args.benchmark, args.size, output_dir) +command = f"{args.benchmark} {args.size} {output_dir}" # For enabling CustomResource, we pass an additional parameter to mount the # correct partition. @@ -278,7 +278,7 @@ board.set_kernel_disk_workload( kernel=Resource("x86-linux-kernel-4.19.83"), # The location of the x86 SPEC CPU 2017 image disk_image=CustomDiskImageResource( - args.image, disk_root_partition=args.partition + args.image, root_partition=args.partition ), readfile_contents=command, ) @@ -288,6 +288,7 @@ def handle_exit(): print("Done bootling Linux") print("Resetting stats at the start of ROI!") m5.stats.reset() + processor.switch() yield False # E.g., continue the simulation. print("Dump stats at the end of the ROI!") m5.stats.dump() @@ -319,7 +320,11 @@ print("Done with the simulation") print() print("Performance statistics:") -print("Simulated time in ROI: " + ((str(simulator.get_roi_ticks()[0])))) +roi_begin_ticks = simulator.get_tick_stopwatch()[0][1] +roi_end_ticks = simulator.get_tick_stopwatch()[1][1] + +print("roi simulated ticks: " + str(roi_end_ticks - roi_begin_ticks)) + print( "Ran a total of", simulator.get_current_tick() / 1e12, "simulated seconds" ) diff --git a/configs/example/gpufs/DisjointNetwork.py b/configs/example/gpufs/DisjointNetwork.py index 1d7f708967..1fbd0dcb15 100644 --- a/configs/example/gpufs/DisjointNetwork.py +++ b/configs/example/gpufs/DisjointNetwork.py @@ -48,7 +48,7 @@ class DisjointSimple(SimpleNetwork): def connectCPU(self, opts, controllers): # Setup parameters for makeTopology call for CPU network - topo_module = import_module("topologies.%s" % opts.cpu_topology) + topo_module = import_module(f"topologies.{opts.cpu_topology}") topo_class = getattr(topo_module, opts.cpu_topology) _topo = topo_class(controllers) _topo.makeTopology(opts, self, SimpleIntLink, SimpleExtLink, Switch) @@ -58,7 +58,7 @@ class DisjointSimple(SimpleNetwork): def connectGPU(self, opts, controllers): # Setup parameters for makeTopology call for GPU network - topo_module = import_module("topologies.%s" % opts.gpu_topology) + topo_module = import_module(f"topologies.{opts.gpu_topology}") topo_class = getattr(topo_module, opts.gpu_topology) _topo = topo_class(controllers) _topo.makeTopology(opts, self, SimpleIntLink, SimpleExtLink, Switch) @@ -84,7 +84,7 @@ class DisjointGarnet(GarnetNetwork): def connectCPU(self, opts, controllers): # Setup parameters for makeTopology call for CPU network - topo_module = import_module("topologies.%s" % opts.cpu_topology) + topo_module = import_module(f"topologies.{opts.cpu_topology}") topo_class = getattr(topo_module, opts.cpu_topology) _topo = topo_class(controllers) _topo.makeTopology( @@ -96,7 +96,7 @@ class DisjointGarnet(GarnetNetwork): def connectGPU(self, opts, controllers): # Setup parameters for makeTopology call - topo_module = import_module("topologies.%s" % opts.gpu_topology) + topo_module = import_module(f"topologies.{opts.gpu_topology}") topo_class = getattr(topo_module, opts.gpu_topology) _topo = topo_class(controllers) _topo.makeTopology( diff --git a/configs/example/gpufs/amd/AmdGPUOptions.py b/configs/example/gpufs/amd/AmdGPUOptions.py index 531249ee84..3d6a8cc48e 100644 --- a/configs/example/gpufs/amd/AmdGPUOptions.py +++ b/configs/example/gpufs/amd/AmdGPUOptions.py @@ -49,7 +49,7 @@ def addAmdGPUOptions(parser): "--cu-per-sqc", type=int, default=4, - help="number of CUs sharing an SQC" " (icache, and thus icache TLB)", + help="number of CUs sharing an SQC (icache, and thus icache TLB)", ) parser.add_argument( "--cu-per-scalar-cache", @@ -102,19 +102,19 @@ def addAmdGPUOptions(parser): "--issue-period", type=int, default=4, - help="Number of cycles per vector instruction issue" " period", + help="Number of cycles per vector instruction issue period", ) parser.add_argument( "--glbmem-wr-bus-width", type=int, default=32, - help="VGPR to Coalescer (Global Memory) data bus width" " in bytes", + help="VGPR to Coalescer (Global Memory) data bus width in bytes", ) parser.add_argument( "--glbmem-rd-bus-width", type=int, default=32, - help="Coalescer to VGPR (Global Memory) data bus width" " in bytes", + help="Coalescer to VGPR (Global Memory) data bus width in bytes", ) # Currently we only support 1 local memory pipe parser.add_argument( @@ -204,20 +204,20 @@ def addAmdGPUOptions(parser): parser.add_argument( "--LocalMemBarrier", action="store_true", - help="Barrier does not wait for writethroughs to " " complete", + help="Barrier does not wait for writethroughs to complete", ) parser.add_argument( "--countPages", action="store_true", - help="Count Page Accesses and output in " " per-CU output files", + help="Count Page Accesses and output in per-CU output files", ) parser.add_argument( - "--TLB-prefetch", type=int, help="prefetch depth for" "TLBs" + "--TLB-prefetch", type=int, help="prefetch depth for TLBs" ) parser.add_argument( "--pf-type", type=str, - help="type of prefetch: " "PF_CU, PF_WF, PF_PHASE, PF_STRIDE", + help="type of prefetch: PF_CU, PF_WF, PF_PHASE, PF_STRIDE", ) parser.add_argument("--pf-stride", type=int, help="set prefetch stride") parser.add_argument( diff --git a/configs/example/gpufs/hip_cookbook.py b/configs/example/gpufs/hip_cookbook.py index 1c22be52da..6a7bb428db 100644 --- a/configs/example/gpufs/hip_cookbook.py +++ b/configs/example/gpufs/hip_cookbook.py @@ -42,7 +42,7 @@ from ruby import Ruby cookbook_runscript = """\ export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH export HSA_ENABLE_INTERRUPT=0 -dmesg -n3 +dmesg -n8 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." @@ -99,18 +99,16 @@ if __name__ == "__m5_main__": # Create temp script to run application if args.app is None: - print("No application given. Use %s -a " % sys.argv[0]) + print(f"No application given. Use {sys.argv[0]} -a ") sys.exit(1) elif args.kernel is None: - print("No kernel path given. Use %s --kernel " % sys.argv[0]) + print(f"No kernel path given. Use {sys.argv[0]} --kernel ") sys.exit(1) elif args.disk_image is None: - print("No disk path given. Use %s --disk-image " % sys.argv[0]) + print(f"No disk path given. Use {sys.argv[0]} --disk-image ") sys.exit(1) elif args.gpu_mmio_trace is None: - print( - "No MMIO trace path. Use %s --gpu-mmio-trace " % sys.argv[0] - ) + print(f"No MMIO trace path. Use {sys.argv[0]} --gpu-mmio-trace ") sys.exit(1) _, tempRunscript = tempfile.mkstemp() diff --git a/configs/example/gpufs/hip_rodinia.py b/configs/example/gpufs/hip_rodinia.py index a6c7c504c1..b8a7858fcd 100644 --- a/configs/example/gpufs/hip_rodinia.py +++ b/configs/example/gpufs/hip_rodinia.py @@ -43,7 +43,7 @@ from ruby import Ruby rodinia_runscript = """\ export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH export HSA_ENABLE_INTERRUPT=0 -dmesg -n3 +dmesg -n8 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." @@ -107,18 +107,16 @@ if __name__ == "__m5_main__": # Create temp script to run application if args.app is None: - print("No application given. Use %s -a " % sys.argv[0]) + print(f"No application given. Use {sys.argv[0]} -a ") sys.exit(1) elif args.kernel is None: - print("No kernel path given. Use %s --kernel " % sys.argv[0]) + print(f"No kernel path given. Use {sys.argv[0]} --kernel ") sys.exit(1) elif args.disk_image is None: - print("No disk path given. Use %s --disk-image " % sys.argv[0]) + print(f"No disk path given. Use {sys.argv[0]} --disk-image ") sys.exit(1) elif args.gpu_mmio_trace is None: - print( - "No MMIO trace path. Use %s --gpu-mmio-trace " % sys.argv[0] - ) + print(f"No MMIO trace path. Use {sys.argv[0]} --gpu-mmio-trace ") sys.exit(1) _, tempRunscript = tempfile.mkstemp() diff --git a/configs/example/gpufs/hip_samples.py b/configs/example/gpufs/hip_samples.py index 0d9263e128..9f83c2550e 100644 --- a/configs/example/gpufs/hip_samples.py +++ b/configs/example/gpufs/hip_samples.py @@ -42,7 +42,7 @@ from ruby import Ruby samples_runscript = """\ export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH export HSA_ENABLE_INTERRUPT=0 -dmesg -n3 +dmesg -n8 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." @@ -97,18 +97,16 @@ if __name__ == "__m5_main__": # Create temp script to run application if args.app is None: - print("No application given. Use %s -a " % sys.argv[0]) + print(f"No application given. Use {sys.argv[0]} -a ") sys.exit(1) elif args.kernel is None: - print("No kernel path given. Use %s --kernel " % sys.argv[0]) + print(f"No kernel path given. Use {sys.argv[0]} --kernel ") sys.exit(1) elif args.disk_image is None: - print("No disk path given. Use %s --disk-image " % sys.argv[0]) + print(f"No disk path given. Use {sys.argv[0]} --disk-image ") sys.exit(1) elif args.gpu_mmio_trace is None: - print( - "No MMIO trace path. Use %s --gpu-mmio-trace " % sys.argv[0] - ) + print(f"No MMIO trace path. Use {sys.argv[0]} --gpu-mmio-trace ") sys.exit(1) _, tempRunscript = tempfile.mkstemp() diff --git a/configs/example/gpufs/runfs.py b/configs/example/gpufs/runfs.py index 781ce8e27c..f8ef70d5a2 100644 --- a/configs/example/gpufs/runfs.py +++ b/configs/example/gpufs/runfs.py @@ -30,6 +30,7 @@ # System includes import argparse import math +import hashlib # gem5 related import m5 @@ -110,13 +111,13 @@ def addRunFSOptions(parser): action="store", type=str, default="16GB", - help="Specify the dGPU physical memory" " size", + 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", + help="Set the number of dGPU directories (memory controllers", ) parser.add_argument( "--dgpu-mem-type", @@ -125,6 +126,17 @@ def addRunFSOptions(parser): help="type of memory to use", ) + # These are the models that are both supported in gem5 and supported + # by the versions of ROCm supported by gem5 in full system mode. For + # other gfx versions there is some support in syscall emulation mode. + parser.add_argument( + "--gpu-device", + default="Vega10", + choices=["Vega10", "MI100", "MI200"], + help="GPU model to run: Vega10 (gfx900), MI100 (gfx908), or " + "MI200 (gfx90a)", + ) + def runGpuFSSystem(args): """ @@ -145,6 +157,11 @@ def runGpuFSSystem(args): math.ceil(float(n_cu) / args.cu_per_scalar_cache) ) + # Verify MMIO trace is valid + mmio_md5 = hashlib.md5(open(args.gpu_mmio_trace, "rb").read()).hexdigest() + if mmio_md5 != "c4ff3326ae8a036e329b8b595c83bd6d": + m5.util.panic("MMIO file does not match gem5 resources") + system = makeGpuFSSystem(args) root = Root( @@ -184,7 +201,7 @@ def runGpuFSSystem(args): break else: print( - "Unknown exit event: %s. Continuing..." % exit_event.getCause() + f"Unknown exit event: {exit_event.getCause()}. Continuing..." ) print( diff --git a/configs/example/gpufs/system/amdgpu.py b/configs/example/gpufs/system/amdgpu.py index 1fd3e2f304..9697e50a04 100644 --- a/configs/example/gpufs/system/amdgpu.py +++ b/configs/example/gpufs/system/amdgpu.py @@ -170,3 +170,18 @@ def connectGPU(system, args): system.pc.south_bridge.gpu.checkpoint_before_mmios = ( args.checkpoint_before_mmios ) + + system.pc.south_bridge.gpu.device_name = args.gpu_device + + if args.gpu_device == "MI100": + system.pc.south_bridge.gpu.DeviceID = 0x738C + system.pc.south_bridge.gpu.SubsystemVendorID = 0x1002 + system.pc.south_bridge.gpu.SubsystemID = 0x0C34 + elif args.gpu_device == "MI200": + system.pc.south_bridge.gpu.DeviceID = 0x740F + system.pc.south_bridge.gpu.SubsystemVendorID = 0x1002 + system.pc.south_bridge.gpu.SubsystemID = 0x0C34 + elif args.gpu_device == "Vega10": + system.pc.south_bridge.gpu.DeviceID = 0x6863 + else: + panic("Unknown GPU device: {}".format(args.gpu_device)) diff --git a/configs/example/gpufs/system/system.py b/configs/example/gpufs/system/system.py index a1b59ef20b..471892945e 100644 --- a/configs/example/gpufs/system/system.py +++ b/configs/example/gpufs/system/system.py @@ -61,7 +61,9 @@ def makeGpuFSSystem(args): panic("Need at least 2GB of system memory to load amdgpu module") # Use the common FSConfig to setup a Linux X86 System - (TestCPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args) + (TestCPUClass, test_mem_mode) = Simulation.getCPUClass(args.cpu_type) + if test_mem_mode == "atomic": + test_mem_mode = "atomic_noncaching" disks = [args.disk_image] if args.second_disk is not None: disks.extend([args.second_disk]) @@ -91,10 +93,11 @@ def makeGpuFSSystem(args): # Create specified number of CPUs. GPUFS really only needs one. system.cpu = [ - X86KvmCPU(clk_domain=system.cpu_clk_domain, cpu_id=i) + TestCPUClass(clk_domain=system.cpu_clk_domain, cpu_id=i) for i in range(args.num_cpus) ] - system.kvm_vm = KvmVM() + if ObjectList.is_kvm_cpu(TestCPUClass): + system.kvm_vm = KvmVM() # Create AMDGPU and attach to southbridge shader = createGPU(system, args) @@ -112,7 +115,8 @@ def makeGpuFSSystem(args): numHWQueues=args.num_hw_queues, walker=hsapp_pt_walker, ) - dispatcher = GPUDispatcher() + dispatcher_exit_events = True if args.exit_at_gpu_kernel > -1 else False + dispatcher = GPUDispatcher(kernel_exit_events=dispatcher_exit_events) cp_pt_walker = VegaPagetableWalker() gpu_cmd_proc = GPUCommandProcessor( hsapp=gpu_hsapp, dispatcher=dispatcher, walker=cp_pt_walker @@ -126,15 +130,55 @@ def makeGpuFSSystem(args): device_ih = AMDGPUInterruptHandler() system.pc.south_bridge.gpu.device_ih = device_ih - # Setup the SDMA engines - sdma0_pt_walker = VegaPagetableWalker() - sdma1_pt_walker = VegaPagetableWalker() + # Setup the SDMA engines depending on device. The MMIO base addresses + # can be found in the driver code under: + # include/asic_reg/sdmaX/sdmaX_Y_Z_offset.h + num_sdmas = 2 + sdma_bases = [] + sdma_sizes = [] + if args.gpu_device == "Vega10": + num_sdmas = 2 + sdma_bases = [0x4980, 0x5180] + sdma_sizes = [0x800] * 2 + elif args.gpu_device == "MI100": + num_sdmas = 8 + sdma_bases = [ + 0x4980, + 0x6180, + 0x78000, + 0x79000, + 0x7A000, + 0x7B000, + 0x7C000, + 0x7D000, + ] + sdma_sizes = [0x1000] * 8 + elif args.gpu_device == "MI200": + num_sdmas = 5 + sdma_bases = [ + 0x4980, + 0x6180, + 0x78000, + 0x79000, + 0x7A000, + ] + sdma_sizes = [0x1000] * 5 + else: + m5.util.panic(f"Unknown GPU device {args.gpu_device}") - sdma0 = SDMAEngine(walker=sdma0_pt_walker) - sdma1 = SDMAEngine(walker=sdma1_pt_walker) + sdma_pt_walkers = [] + sdma_engines = [] + for sdma_idx in range(num_sdmas): + sdma_pt_walker = VegaPagetableWalker() + sdma_engine = SDMAEngine( + walker=sdma_pt_walker, + mmio_base=sdma_bases[sdma_idx], + mmio_size=sdma_sizes[sdma_idx], + ) + sdma_pt_walkers.append(sdma_pt_walker) + sdma_engines.append(sdma_engine) - system.pc.south_bridge.gpu.sdma0 = sdma0 - system.pc.south_bridge.gpu.sdma1 = sdma1 + system.pc.south_bridge.gpu.sdmas = sdma_engines # Setup PM4 packet processor pm4_pkt_proc = PM4PacketProcessor() @@ -152,22 +196,22 @@ def makeGpuFSSystem(args): 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) + for sdma in sdma_engines: + system._dma_ports.append(sdma) 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) + for sdma_pt_walker in sdma_pt_walkers: + system._dma_ports.append(sdma_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 + for sdma in sdma_engines: + sdma.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 diff --git a/configs/example/gpufs/vega10.py b/configs/example/gpufs/vega10.py new file mode 100644 index 0000000000..9eff5a2974 --- /dev/null +++ b/configs/example/gpufs/vega10.py @@ -0,0 +1,153 @@ +# Copyright (c) 2022-2023 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_without_checkpoint = """\ +export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH +export HSA_ENABLE_INTERRUPT=0 +dmesg -n8 +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 +""" + +demo_runscript_with_checkpoint = """\ +export LD_LIBRARY_PATH=/opt/rocm/lib:$LD_LIBRARY_PATH +export HSA_ENABLE_INTERRUPT=0 +dmesg -n8 +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 +/sbin/m5 checkpoint +./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" + ) + + +def runVegaGPUFS(cpu_type): + 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() + demo_runscript = "" + + # Create temp script to run application + if args.app is None: + print(f"No application given. Use {sys.argv[0]} -a ") + sys.exit(1) + elif args.kernel is None: + print(f"No kernel path given. Use {sys.argv[0]} --kernel ") + sys.exit(1) + elif args.disk_image is None: + print(f"No disk path given. Use {sys.argv[0]} --disk-image ") + sys.exit(1) + elif args.gpu_mmio_trace is None: + print(f"No MMIO trace path. Use {sys.argv[0]} --gpu-mmio-trace ") + sys.exit(1) + elif not os.path.isfile(args.app): + print("Could not find applcation", args.app) + sys.exit(1) + + # Choose runscript Based on whether any checkpointing args are set + if args.checkpoint_dir is not None: + demo_runscript = demo_runscript_with_checkpoint + else: + demo_runscript = demo_runscript_without_checkpoint + + 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 = cpu_type + 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/gpufs/vega10_atomic.py b/configs/example/gpufs/vega10_atomic.py new file mode 100644 index 0000000000..4ff2cc2e72 --- /dev/null +++ b/configs/example/gpufs/vega10_atomic.py @@ -0,0 +1,32 @@ +# Copyright (c) 2023 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 vega10 + +vega10.runVegaGPUFS("AtomicSimpleCPU") diff --git a/configs/example/gpufs/vega10_kvm.py b/configs/example/gpufs/vega10_kvm.py index 48e2d69516..39dc5e0110 100644 --- a/configs/example/gpufs/vega10_kvm.py +++ b/configs/example/gpufs/vega10_kvm.py @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Advanced Micro Devices, Inc. +# Copyright (c) 2022-2023 Advanced Micro Devices, Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -27,104 +27,6 @@ # 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 +import vega10 -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_INTERRUPT=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 " % sys.argv[0]) - sys.exit(1) - elif args.kernel is None: - print("No kernel path given. Use %s --kernel " % sys.argv[0]) - sys.exit(1) - elif args.disk_image is None: - print("No disk path given. Use %s --disk-image " % sys.argv[0]) - sys.exit(1) - elif args.gpu_mmio_trace is None: - print( - "No MMIO trace path. Use %s --gpu-mmio-trace " % 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) +vega10.runVegaGPUFS("X86KvmCPU") diff --git a/configs/example/hsaTopology.py b/configs/example/hsaTopology.py index 691e8c2a58..909b9ef519 100644 --- a/configs/example/hsaTopology.py +++ b/configs/example/hsaTopology.py @@ -118,11 +118,11 @@ def createVegaTopology(options): # Populate CPU node properties node_prop = ( - "cpu_cores_count %s\n" % options.num_cpus + f"cpu_cores_count {options.num_cpus}\n" + "simd_count 0\n" + "mem_banks_count 1\n" + "caches_count 0\n" - + "io_links_count %s\n" % io_links + + f"io_links_count {io_links}\n" + "cpu_core_id_base 0\n" + "simd_id_base 0\n" + "max_waves_per_simd 0\n" @@ -200,8 +200,8 @@ def createVegaTopology(options): "cpu_cores_count 0\n" + "simd_count 256\n" + "mem_banks_count 1\n" - + "caches_count %s\n" % caches - + "io_links_count %s\n" % io_links + + f"caches_count {caches}\n" + + f"io_links_count {io_links}\n" + "cpu_core_id_base 0\n" + "simd_id_base 2147487744\n" + "max_waves_per_simd 10\n" @@ -212,11 +212,11 @@ def createVegaTopology(options): + "simd_arrays_per_engine 1\n" + "cu_per_simd_array 16\n" + "simd_per_cu 4\n" - + "max_slots_scratch_cu %s\n" % cu_scratch + + f"max_slots_scratch_cu {cu_scratch}\n" + "vendor_id 4098\n" + "device_id 26720\n" + "location_id 1024\n" - + "drm_render_minor %s\n" % drm_num + + f"drm_render_minor {drm_num}\n" + "hive_id 0\n" + "num_sdma_engines 2\n" + "num_sdma_xgmi_engines 0\n" @@ -313,11 +313,11 @@ def createFijiTopology(options): # Populate CPU node properties node_prop = ( - "cpu_cores_count %s\n" % options.num_cpus + f"cpu_cores_count {options.num_cpus}\n" + "simd_count 0\n" + "mem_banks_count 1\n" + "caches_count 0\n" - + "io_links_count %s\n" % io_links + + f"io_links_count {io_links}\n" + "cpu_core_id_base 0\n" + "simd_id_base 0\n" + "max_waves_per_simd 0\n" @@ -392,33 +392,30 @@ def createFijiTopology(options): # Populate GPU node properties node_prop = ( "cpu_cores_count 0\n" - + "simd_count %s\n" - % (options.num_compute_units * options.simds_per_cu) + + f"simd_count {options.num_compute_units * options.simds_per_cu}\n" + "mem_banks_count 1\n" - + "caches_count %s\n" % caches - + "io_links_count %s\n" % io_links + + f"caches_count {caches}\n" + + f"io_links_count {io_links}\n" + "cpu_core_id_base 0\n" + "simd_id_base 2147487744\n" - + "max_waves_per_simd %s\n" % options.wfs_per_simd - + "lds_size_in_kb %s\n" % int(options.lds_size / 1024) + + f"max_waves_per_simd {options.wfs_per_simd}\n" + + f"lds_size_in_kb {int(options.lds_size / 1024)}\n" + "gds_size_in_kb 0\n" - + "wave_front_size %s\n" % options.wf_size + + f"wave_front_size {options.wf_size}\n" + "array_count 4\n" - + "simd_arrays_per_engine %s\n" % options.sa_per_complex - + "cu_per_simd_array %s\n" % options.cu_per_sa - + "simd_per_cu %s\n" % options.simds_per_cu + + f"simd_arrays_per_engine {options.sa_per_complex}\n" + + f"cu_per_simd_array {options.cu_per_sa}\n" + + f"simd_per_cu {options.simds_per_cu}\n" + "max_slots_scratch_cu 32\n" + "vendor_id 4098\n" + "device_id 29440\n" + "location_id 512\n" - + "drm_render_minor %s\n" % drm_num - + "max_engine_clk_fcompute %s\n" - % int(toFrequency(options.gpu_clock) / 1e6) + + f"drm_render_minor {drm_num}\n" + + f"max_engine_clk_fcompute {int(toFrequency(options.gpu_clock) / 1000000.0)}\n" + "local_mem_size 4294967296\n" + "fw_version 730\n" + "capability 4736\n" - + "max_engine_clk_ccompute %s\n" - % int(toFrequency(options.CPUClock) / 1e6) + + f"max_engine_clk_ccompute {int(toFrequency(options.CPUClock) / 1000000.0)}\n" ) file_append((node_dir, "properties"), node_prop) @@ -484,34 +481,31 @@ def createCarrizoTopology(options): # populate global node properties # NOTE: SIMD count triggers a valid GPU agent creation node_prop = ( - "cpu_cores_count %s\n" % options.num_cpus - + "simd_count %s\n" - % (options.num_compute_units * options.simds_per_cu) - + "mem_banks_count %s\n" % mem_banks_cnt + f"cpu_cores_count {options.num_cpus}\n" + + f"simd_count {options.num_compute_units * options.simds_per_cu}\n" + + f"mem_banks_count {mem_banks_cnt}\n" + "caches_count 0\n" + "io_links_count 0\n" + "cpu_core_id_base 16\n" + "simd_id_base 2147483648\n" - + "max_waves_per_simd %s\n" % options.wfs_per_simd - + "lds_size_in_kb %s\n" % int(options.lds_size / 1024) + + f"max_waves_per_simd {options.wfs_per_simd}\n" + + f"lds_size_in_kb {int(options.lds_size / 1024)}\n" + "gds_size_in_kb 0\n" - + "wave_front_size %s\n" % options.wf_size + + f"wave_front_size {options.wf_size}\n" + "array_count 1\n" - + "simd_arrays_per_engine %s\n" % options.sa_per_complex - + "cu_per_simd_array %s\n" % options.cu_per_sa - + "simd_per_cu %s\n" % options.simds_per_cu + + f"simd_arrays_per_engine {options.sa_per_complex}\n" + + f"cu_per_simd_array {options.cu_per_sa}\n" + + f"simd_per_cu {options.simds_per_cu}\n" + "max_slots_scratch_cu 32\n" + "vendor_id 4098\n" - + "device_id %s\n" % device_id + + f"device_id {device_id}\n" + "location_id 8\n" - + "drm_render_minor %s\n" % drm_num - + "max_engine_clk_fcompute %s\n" - % int(toFrequency(options.gpu_clock) / 1e6) + + f"drm_render_minor {drm_num}\n" + + f"max_engine_clk_fcompute {int(toFrequency(options.gpu_clock) / 1000000.0)}\n" + "local_mem_size 0\n" + "fw_version 699\n" + "capability 4738\n" - + "max_engine_clk_ccompute %s\n" - % int(toFrequency(options.CPUClock) / 1e6) + + f"max_engine_clk_ccompute {int(toFrequency(options.CPUClock) / 1000000.0)}\n" ) file_append((node_dir, "properties"), node_prop) diff --git a/configs/example/lupv/run_lupv.py b/configs/example/lupv/run_lupv.py index 0056cf8bb4..d92ea3fa3f 100644 --- a/configs/example/lupv/run_lupv.py +++ b/configs/example/lupv/run_lupv.py @@ -113,6 +113,4 @@ print("Beginning simulation!") exit_event = m5.simulate(args.max_ticks) -print( - "Exiting @ tick {} because {}.".format(m5.curTick(), exit_event.getCause()) -) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}.") diff --git a/configs/example/memcheck.py b/configs/example/memcheck.py index a50644b2b1..aee2ef74d0 100644 --- a/configs/example/memcheck.py +++ b/configs/example/memcheck.py @@ -330,7 +330,7 @@ def make_cache_level(ncaches, prototypes, level, next_cache): make_cache_level(cachespec, cache_proto, len(cachespec), None) # Connect the lowest level crossbar to the memory -last_subsys = getattr(system, "l%dsubsys0" % len(cachespec)) +last_subsys = getattr(system, f"l{len(cachespec)}subsys0") last_subsys.xbar.mem_side_ports = system.physmem.port last_subsys.xbar.point_of_coherency = True diff --git a/configs/example/memtest.py b/configs/example/memtest.py index 58d762dc60..0cbbab5b4f 100644 --- a/configs/example/memtest.py +++ b/configs/example/memtest.py @@ -211,8 +211,7 @@ else: if numtesters(cachespec, testerspec) > block_size: print( - "Error: Limited to %s testers because of false sharing" - % (block_size) + f"Error: Limited to {block_size} testers because of false sharing" ) sys.exit(1) @@ -351,7 +350,7 @@ make_cache_level(cachespec, cache_proto, len(cachespec), None) # Connect the lowest level crossbar to the last-level cache and memory # controller -last_subsys = getattr(system, "l%dsubsys0" % len(cachespec)) +last_subsys = getattr(system, f"l{len(cachespec)}subsys0") last_subsys.xbar.point_of_coherency = True if args.noncoherent_cache: system.llc = NoncoherentCache( diff --git a/configs/example/read_config.py b/configs/example/read_config.py index b52a73d1fa..40c20ef501 100644 --- a/configs/example/read_config.py +++ b/configs/example/read_config.py @@ -68,8 +68,7 @@ sim_object_classes_by_name = { def no_parser(cls, flags, param): raise Exception( - "Can't parse string: %s for parameter" - " class: %s" % (str(param), cls.__name__) + f"Can't parse string: {str(param)} for parameter class: {cls.__name__}" ) @@ -114,7 +113,7 @@ def memory_bandwidth_parser(cls, flags, param): value = 1.0 / float(param) # Convert to byte/s value = ticks.fromSeconds(value) - return cls("%fB/s" % value) + return cls(f"{value:f}B/s") # These parameters have trickier parsing from .ini files than might be @@ -201,8 +200,7 @@ class ConfigManager(object): if object_type not in sim_object_classes_by_name: raise Exception( - "No SimObject type %s is available to" - " build: %s" % (object_type, object_name) + f"No SimObject type {object_type} is available to build: {object_name}" ) object_class = sim_object_classes_by_name[object_type] @@ -479,7 +477,7 @@ class ConfigIniFile(ConfigFile): if object_name == "root": return child_name else: - return "%s.%s" % (object_name, child_name) + return f"{object_name}.{child_name}" return [(name, make_path(name)) for name in child_names] diff --git a/configs/example/riscv/fs_linux.py b/configs/example/riscv/fs_linux.py index 1a98126e92..949c7e2623 100644 --- a/configs/example/riscv/fs_linux.py +++ b/configs/example/riscv/fs_linux.py @@ -91,7 +91,7 @@ from common import Options def generateMemNode(state, mem_range): - node = FdtNode("memory@%x" % int(mem_range.start)) + node = FdtNode(f"memory@{int(mem_range.start):x}") node.append(FdtPropertyStrings("device_type", ["memory"])) node.append( FdtPropertyWords( @@ -187,6 +187,7 @@ system.platform = HiFive() # RTCCLK (Set to 100MHz for faster simulation) system.platform.rtc = RiscvRTC(frequency=Frequency("100MHz")) system.platform.clint.int_pin = system.platform.rtc.int_pin +system.platform.pci_host.pio = system.iobus.mem_side_ports # VirtIOMMIO if args.disk_image: @@ -236,8 +237,6 @@ system.cpu_clk_domain = SrcClockDomain( clock=args.cpu_clock, voltage_domain=system.cpu_voltage_domain ) -system.workload.object_file = args.kernel - # NOTE: Not yet tested if args.script is not None: system.readfile = args.script diff --git a/configs/example/se.py b/configs/example/se.py index 2372cf0efe..c185f09e5a 100644 --- a/configs/example/se.py +++ b/configs/example/se.py @@ -1,16 +1,4 @@ -# Copyright (c) 2012-2013 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) 2006-2008 The Regents of The University of Michigan +# Copyright (c) 2023 The Regents of the University of California # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -36,253 +24,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. -# Simple test script -# -# "m5 test.py" +from m5.util import fatal -import argparse -import sys -import os - -import m5 -from m5.defines import buildEnv -from m5.objects import * -from m5.params import NULL -from m5.util import addToPath, fatal, warn -from gem5.isas import ISA -from gem5.runtime import get_runtime_isa - -addToPath("../") - -from ruby import Ruby - -from common import Options -from common import Simulation -from common import CacheConfig -from common import CpuConfig -from common import ObjectList -from common import MemConfig -from common.FileSystemConfig import config_filesystem -from common.Caches import * -from common.cpu2000 import * - - -def get_processes(args): - """Interprets provided args and returns a list of processes""" - - multiprocesses = [] - inputs = [] - outputs = [] - errouts = [] - pargs = [] - - workloads = args.cmd.split(";") - if args.input != "": - inputs = args.input.split(";") - if args.output != "": - outputs = args.output.split(";") - if args.errout != "": - errouts = args.errout.split(";") - if args.options != "": - pargs = args.options.split(";") - - idx = 0 - for wrkld in workloads: - process = Process(pid=100 + idx) - process.executable = wrkld - process.cwd = os.getcwd() - process.gid = os.getgid() - - if args.env: - with open(args.env, "r") as f: - process.env = [line.rstrip() for line in f] - - if len(pargs) > idx: - process.cmd = [wrkld] + pargs[idx].split() - else: - process.cmd = [wrkld] - - if len(inputs) > idx: - process.input = inputs[idx] - if len(outputs) > idx: - process.output = outputs[idx] - if len(errouts) > idx: - process.errout = errouts[idx] - - multiprocesses.append(process) - idx += 1 - - if args.smt: - assert args.cpu_type == "DerivO3CPU" - return multiprocesses, idx - else: - return multiprocesses, 1 - - -parser = argparse.ArgumentParser() -Options.addCommonOptions(parser) -Options.addSEOptions(parser) - -if "--ruby" in sys.argv: - Ruby.define_options(parser) - -args = parser.parse_args() - -multiprocesses = [] -numThreads = 1 - -if args.bench: - apps = args.bench.split("-") - if len(apps) != args.num_cpus: - print("number of benchmarks not equal to set num_cpus!") - sys.exit(1) - - for app in apps: - try: - if get_runtime_isa() == ISA.ARM: - exec( - "workload = %s('arm_%s', 'linux', '%s')" - % (app, args.arm_iset, args.spec_input) - ) - else: - # TARGET_ISA has been removed, but this is missing a ], so it - # has incorrect syntax and wasn't being used anyway. - exec( - "workload = %s(buildEnv['TARGET_ISA', 'linux', '%s')" - % (app, args.spec_input) - ) - multiprocesses.append(workload.makeProcess()) - except: - print( - "Unable to find workload for %s: %s" - % (get_runtime_isa().name(), app), - file=sys.stderr, - ) - sys.exit(1) -elif args.cmd: - multiprocesses, numThreads = get_processes(args) -else: - print("No workload specified. Exiting!\n", file=sys.stderr) - sys.exit(1) - - -(CPUClass, test_mem_mode, FutureClass) = Simulation.setCPUClass(args) -CPUClass.numThreads = numThreads - -# Check -- do not allow SMT with multiple CPUs -if args.smt and args.num_cpus > 1: - fatal("You cannot use SMT with multiple CPUs!") - -np = args.num_cpus -mp0_path = multiprocesses[0].executable -system = System( - cpu=[CPUClass(cpu_id=i) for i in range(np)], - mem_mode=test_mem_mode, - mem_ranges=[AddrRange(args.mem_size)], - cache_line_size=args.cacheline_size, +fatal( + "The 'configs/example/se.py' script has been deprecated. It can be " + "found in 'configs/deprecated/example' if required. Its usage should be " + "avoided as it will be removed in future releases of gem5." ) - -if numThreads > 1: - system.multi_thread = True - -# Create a top-level voltage domain -system.voltage_domain = VoltageDomain(voltage=args.sys_voltage) - -# Create a source clock for the system and set the clock period -system.clk_domain = SrcClockDomain( - clock=args.sys_clock, voltage_domain=system.voltage_domain -) - -# Create a CPU voltage domain -system.cpu_voltage_domain = VoltageDomain() - -# Create a separate clock domain for the CPUs -system.cpu_clk_domain = SrcClockDomain( - clock=args.cpu_clock, voltage_domain=system.cpu_voltage_domain -) - -# If elastic tracing is enabled, then configure the cpu and attach the elastic -# trace probe -if args.elastic_trace_en: - CpuConfig.config_etrace(CPUClass, system.cpu, args) - -# All cpus belong to a common cpu_clk_domain, therefore running at a common -# frequency. -for cpu in system.cpu: - cpu.clk_domain = system.cpu_clk_domain - -if ObjectList.is_kvm_cpu(CPUClass) or ObjectList.is_kvm_cpu(FutureClass): - if buildEnv["USE_X86_ISA"]: - system.kvm_vm = KvmVM() - system.m5ops_base = 0xFFFF0000 - for process in multiprocesses: - process.useArchPT = True - process.kvmInSE = True - else: - fatal("KvmCPU can only be used in SE mode with x86") - -# Sanity check -if args.simpoint_profile: - if not ObjectList.is_noncaching_cpu(CPUClass): - fatal("SimPoint/BPProbe should be done with an atomic cpu") - if np > 1: - fatal("SimPoint generation not supported with more than one CPUs") - -for i in range(np): - if args.smt: - system.cpu[i].workload = multiprocesses - elif len(multiprocesses) == 1: - system.cpu[i].workload = multiprocesses[0] - else: - system.cpu[i].workload = multiprocesses[i] - - if args.simpoint_profile: - system.cpu[i].addSimPointProbe(args.simpoint_interval) - - if args.checker: - system.cpu[i].addCheckerCpu() - - if args.bp_type: - bpClass = ObjectList.bp_list.get(args.bp_type) - system.cpu[i].branchPred = bpClass() - - if args.indirect_bp_type: - indirectBPClass = ObjectList.indirect_bp_list.get( - args.indirect_bp_type - ) - system.cpu[i].branchPred.indirectBranchPred = indirectBPClass() - - system.cpu[i].createThreads() - -if args.ruby: - Ruby.create_system(args, False, system) - assert args.num_cpus == len(system.ruby._cpu_ports) - - system.ruby.clk_domain = SrcClockDomain( - clock=args.ruby_clock, voltage_domain=system.voltage_domain - ) - for i in range(np): - ruby_port = system.ruby._cpu_ports[i] - - # Create the interrupt controller and connect its ports to Ruby - # Note that the interrupt controller is always present but only - # in x86 does it have message ports that need to be connected - system.cpu[i].createInterruptController() - - # Connect the cpu's cache ports to Ruby - ruby_port.connectCpuPorts(system.cpu[i]) -else: - MemClass = Simulation.setMemClass(args) - system.membus = SystemXBar() - system.system_port = system.membus.cpu_side_ports - CacheConfig.config_cache(args, system) - MemConfig.config_mem(args, system) - config_filesystem(system, args) - -system.workload = SEWorkload.init_compatible(mp0_path) - -if args.wait_gdb: - system.workload.wait_for_remote_gdb = True - -root = Root(full_system=False, system=system) -Simulation.run(args, root, system, FutureClass) diff --git a/configs/example/sst/riscv_fs.py b/configs/example/sst/riscv_fs.py index fb22f29190..fc8f8618c4 100644 --- a/configs/example/sst/riscv_fs.py +++ b/configs/example/sst/riscv_fs.py @@ -35,7 +35,7 @@ import argparse def generateMemNode(state, mem_range): - node = FdtNode("memory@%x" % int(mem_range.start)) + node = FdtNode(f"memory@{int(mem_range.start):x}") node.append(FdtPropertyStrings("device_type", ["memory"])) node.append( FdtPropertyWords( diff --git a/configs/learning_gem5/part1/caches.py b/configs/learning_gem5/part1/caches.py index 9bb06ab2e6..3f7d26ed21 100644 --- a/configs/learning_gem5/part1/caches.py +++ b/configs/learning_gem5/part1/caches.py @@ -75,7 +75,7 @@ class L1ICache(L1Cache): size = "16kB" SimpleOpts.add_option( - "--l1i_size", help="L1 instruction cache size. Default: %s" % size + "--l1i_size", help=f"L1 instruction cache size. Default: {size}" ) def __init__(self, opts=None): @@ -96,7 +96,7 @@ class L1DCache(L1Cache): size = "64kB" SimpleOpts.add_option( - "--l1d_size", help="L1 data cache size. Default: %s" % size + "--l1d_size", help=f"L1 data cache size. Default: {size}" ) def __init__(self, opts=None): @@ -122,9 +122,7 @@ class L2Cache(Cache): mshrs = 20 tgts_per_mshr = 12 - SimpleOpts.add_option( - "--l2_size", help="L2 cache size. Default: %s" % size - ) + SimpleOpts.add_option("--l2_size", help=f"L2 cache size. Default: {size}") def __init__(self, opts=None): super(L2Cache, self).__init__() diff --git a/configs/learning_gem5/part3/ruby_test.py b/configs/learning_gem5/part3/ruby_test.py index d0cc1be613..e46f07bb0a 100644 --- a/configs/learning_gem5/part3/ruby_test.py +++ b/configs/learning_gem5/part3/ruby_test.py @@ -78,6 +78,4 @@ m5.instantiate() print("Beginning simulation!") exit_event = m5.simulate() -print( - "Exiting @ tick {} because {}".format(m5.curTick(), exit_event.getCause()) -) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/learning_gem5/part3/simple_ruby.py b/configs/learning_gem5/part3/simple_ruby.py index b62a7195c8..f3f84353e8 100644 --- a/configs/learning_gem5/part3/simple_ruby.py +++ b/configs/learning_gem5/part3/simple_ruby.py @@ -110,6 +110,4 @@ m5.instantiate() print("Beginning simulation!") exit_event = m5.simulate() -print( - "Exiting @ tick {} because {}".format(m5.curTick(), exit_event.getCause()) -) +print(f"Exiting @ tick {m5.curTick()} because {exit_event.getCause()}") diff --git a/configs/ruby/CHI.py b/configs/ruby/CHI.py index df97b923ae..96537e558a 100644 --- a/configs/ruby/CHI.py +++ b/configs/ruby/CHI.py @@ -280,6 +280,6 @@ def create_system( elif options.topology in ["Crossbar", "Pt2Pt"]: topology = create_topology(network_cntrls, options) else: - m5.fatal("%s not supported!" % options.topology) + m5.fatal(f"{options.topology} not supported!") return (cpu_sequencers, mem_cntrls, topology) diff --git a/configs/ruby/CHI_config.py b/configs/ruby/CHI_config.py index 6d2084bc7b..4f2580c373 100644 --- a/configs/ruby/CHI_config.py +++ b/configs/ruby/CHI_config.py @@ -428,7 +428,7 @@ class CPUSequencerWrapper: cpu.icache_port = self.inst_seq.in_ports for p in cpu._cached_ports: if str(p) != "icache_port": - exec("cpu.%s = self.data_seq.in_ports" % p) + exec(f"cpu.{p} = self.data_seq.in_ports") cpu.connectUncachedPorts( self.data_seq.in_ports, self.data_seq.interrupt_out_port ) diff --git a/configs/ruby/Ruby.py b/configs/ruby/Ruby.py index 3ca7b95140..d3c2efbb3f 100644 --- a/configs/ruby/Ruby.py +++ b/configs/ruby/Ruby.py @@ -120,8 +120,8 @@ def define_options(parser): ) protocol = buildEnv["PROTOCOL"] - exec("from . import %s" % protocol) - eval("%s.define_options(parser)" % protocol) + exec(f"from . import {protocol}") + eval(f"{protocol}.define_options(parser)") Network.define_options(parser) @@ -207,8 +207,8 @@ def create_topology(controllers, options): found in configs/topologies/BaseTopology.py This is a wrapper for the legacy topologies. """ - exec("import topologies.%s as Topo" % options.topology) - topology = eval("Topo.%s(controllers)" % options.topology) + exec(f"import topologies.{options.topology} as Topo") + topology = eval(f"Topo.{options.topology}(controllers)") return topology @@ -242,7 +242,7 @@ def create_system( cpus = system.cpu protocol = buildEnv["PROTOCOL"] - exec("from . import %s" % protocol) + exec(f"from . import {protocol}") try: (cpu_sequencers, dir_cntrls, topology) = eval( "%s.create_system(options, full_system, system, dma_ports,\ @@ -250,7 +250,7 @@ def create_system( % protocol ) except: - print("Error: could not create sytem for ruby protocol %s" % protocol) + print(f"Error: could not create sytem for ruby protocol {protocol}") raise # Create the network topology diff --git a/configs/topologies/CustomMesh.py b/configs/topologies/CustomMesh.py index 088e4b9cfe..c62b39a9c2 100644 --- a/configs/topologies/CustomMesh.py +++ b/configs/topologies/CustomMesh.py @@ -325,9 +325,7 @@ class CustomMesh(SimpleTopology): rni_io_params = check_same(type(n).NoC_Params, rni_io_params) else: fatal( - "topologies.CustomMesh: {} not supported".format( - n.__class__.__name__ - ) + f"topologies.CustomMesh: {n.__class__.__name__} not supported" ) # Create all mesh routers @@ -420,11 +418,11 @@ class CustomMesh(SimpleTopology): if pair_debug: print(c.path()) for r in c.addr_ranges: - print("%s" % r) + print(f"{r}") for p in c._pairing: print("\t" + p.path()) for r in p.addr_ranges: - print("\t%s" % r) + print(f"\t{r}") # all must be paired for c in all_cache: @@ -516,8 +514,8 @@ class CustomMesh(SimpleTopology): assert len(c._pairing) == pairing_check print(c.path()) for r in c.addr_ranges: - print("%s" % r) + print(f"{r}") for p in c._pairing: print("\t" + p.path()) for r in p.addr_ranges: - print("\t%s" % r) + print(f"\t{r}") diff --git a/ext/drampower/SConscript b/ext/drampower/SConscript index 870d0504c7..38acbf4d06 100644 --- a/ext/drampower/SConscript +++ b/ext/drampower/SConscript @@ -41,7 +41,7 @@ import os Import('env') -env.Prepend(CPPPATH=Dir('./src')) +env.Prepend(CPPPATH=Dir('./src').srcnode()) # Add the appropriate files for the library drampower_files = [] diff --git a/ext/dramsim2/SConscript b/ext/dramsim2/SConscript index 95b999dc4c..c2965384d5 100644 --- a/ext/dramsim2/SConscript +++ b/ext/dramsim2/SConscript @@ -59,7 +59,7 @@ DRAMFile('AddressMapping.cpp') DRAMFile('Bank.cpp') DRAMFile('BankState.cpp') DRAMFile('BusPacket.cpp') -DRAMFile('ClockDoenv.cpp') +DRAMFile('ClockDomain.cpp') DRAMFile('CommandQueue.cpp') DRAMFile('IniReader.cpp') DRAMFile('MemoryController.cpp') @@ -85,6 +85,6 @@ dramenv.Append(CCFLAGS=['-DNO_STORAGE']) dramenv.Library('dramsim2', [dramenv.SharedObject(f) for f in dram_files]) -env.Prepend(CPPPATH=Dir('.')) +env.Prepend(CPPPATH=Dir('.').srcnode()) env.Append(LIBS=['dramsim2']) env.Prepend(LIBPATH=[Dir('.')]) diff --git a/ext/dramsim3/SConscript b/ext/dramsim3/SConscript index b7178161f7..6be9690db2 100644 --- a/ext/dramsim3/SConscript +++ b/ext/dramsim3/SConscript @@ -56,12 +56,12 @@ 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') - env.Prepend(CPPPATH=Dir('.')) + env.Prepend(CPPPATH=Dir('.').srcnode()) env.Append(LIBS=['dramsim3', 'superlu_mt_OPENMP', 'm', 'f77blas', 'atlas', 'gomp'], LIBPATH=[dramsim_path, superlu_path]) else: - env.Prepend(CPPPATH=Dir('.')) + env.Prepend(CPPPATH=Dir('.').srcnode()) # a littel hacky but can get a shared library working env.Append(LIBS=['dramsim3', 'gomp'], LIBPATH=[dramsim_path], # compile-time lookup diff --git a/ext/dramsys/README b/ext/dramsys/README new file mode 100644 index 0000000000..477da52895 --- /dev/null +++ b/ext/dramsys/README @@ -0,0 +1,10 @@ +Follow these steps to get DRAMSys as part of gem5 + +1. Go to ext/dramsys (this directory) +2. Clone DRAMSys: 'git clone --recursive git@github.com:tukl-msd/DRAMSys.git DRAMSys' +3. Change directory to DRAMSys: 'cd DRAMSys' +4. Checkout the correct commit: 'git checkout -b gem5 09f6dcbb91351e6ee7cadfc7bc8b29d97625db8f' + +If you wish to run a simulation using the gem5 processor cores, make sure to enable the storage mode in DRAMSys. +This is done by setting the value of the "StoreMode" key to "Store" in the base configuration file. +Those configuration file can be found in 'DRAMSys/library/resources/configs/simulator'. diff --git a/ext/dramsys/SConscript b/ext/dramsys/SConscript new file mode 100644 index 0000000000..d6ea27e0d1 --- /dev/null +++ b/ext/dramsys/SConscript @@ -0,0 +1,96 @@ +# 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: 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 os + +Import('env') + +build_root = Dir('../..').abspath +src_root = Dir('DRAMSys/DRAMSys/library').srcnode().abspath + +# See if we got a cloned DRAMSys repo as a subdirectory and set the +# HAVE_DRAMSys flag accordingly +if not os.path.exists(Dir('.').srcnode().abspath + '/DRAMSys'): + env['HAVE_DRAMSYS'] = False + Return() + +env['HAVE_DRAMSYS'] = True + +dramsys_files = [] +dramsys_configuration_files = [] + +dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/controller")) +for root, dirs, files in os.walk(f"{src_root}/src/controller", topdown=False): + for dir in dirs: + dramsys_files.extend(Glob("%s/*.cpp" % os.path.join(root, dir))) + +dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/simulation")) +for root, dirs, files in os.walk(f"{src_root}/src/simulation", topdown=False): + for dir in dirs: + dramsys_files.extend(Glob("%s/*.cpp" % os.path.join(root, dir))) + +dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/configuration")) +for root, dirs, files in os.walk(f"{src_root}/src/configuration", topdown=False): + for dir in dirs: + dramsys_files.extend(Glob("%s/*.cpp" % os.path.join(root, dir))) + +dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/error")) +dramsys_files.extend(Glob(f"{src_root}/src/error/ECC/Bit.cpp")) +dramsys_files.extend(Glob(f"{src_root}/src/error/ECC/ECC.cpp")) +dramsys_files.extend(Glob(f"{src_root}/src/error/ECC/Word.cpp")) + +dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/common")) +dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/common/configuration")) +dramsys_files.extend(Glob("%s/*.cpp" % f"{src_root}/src/common/configuration/memspec")) +dramsys_files.extend(Glob("%s/*.c" % f"{src_root}/src/common/third_party/sqlite-amalgamation")) + +env.Prepend(CPPPATH=[ + src_root + "/src", + src_root + "/src/common/configuration", + src_root + "/src/common/third_party/nlohmann/include", +]) + +env.Prepend(CPPDEFINES=[("DRAMSysResourceDirectory", '\\"' + os.getcwd() + '/resources' + '\\"')]) +env.Prepend(CPPDEFINES=[("SYSTEMC_VERSION", 20191203)]) + +dramsys = env.Clone() + +if '-Werror' in dramsys['CCFLAGS']: + dramsys['CCFLAGS'].remove('-Werror') + +dramsys.Prepend(CPPPATH=[ + src_root + "/src/common/third_party/sqlite-amalgamation", + build_root + "/systemc/ext" +]) + +dramsys.Prepend(CPPDEFINES=[("SQLITE_ENABLE_RTREE", "1")]) + +dramsys_configuration = env.Clone() + +dramsys.Library('dramsys', dramsys_files) + +env.Append(LIBS=['dramsys', 'dl']) +env.Append(LIBPATH=[Dir('.')]) diff --git a/ext/fputils/SConscript b/ext/fputils/SConscript index 6a8e44f4af..bc158c2936 100644 --- a/ext/fputils/SConscript +++ b/ext/fputils/SConscript @@ -30,7 +30,7 @@ Import('env') -env.Prepend(CPPPATH=Dir('./include')) +env.Prepend(CPPPATH=Dir('./include').srcnode()) fpenv = env.Clone() diff --git a/ext/gdbremote/signals.hh b/ext/gdbremote/signals.hh new file mode 100644 index 0000000000..11835e6f5a --- /dev/null +++ b/ext/gdbremote/signals.hh @@ -0,0 +1,181 @@ +//===-- Generated From GDBRemoteSignals.cpp ------------------------===// +// +// Part of the LLVM Project, +// under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------===// + +#include + +#ifndef __BASE_GDB_SIGNALS_HH__ +#define __BASE_GDB_SIGNALS_HH__ + +/* +These signals definitions are produced from LLVM's + lldb/source/Plugins/Process/Utility/GDBRemoteSignals.cpp +*/ +namespace gem5{ + enum class GDBSignal : uint8_t + { + ZERO = 0, //Signal 0 + HUP = 1, //hangup + INT = 2, //interrupt + QUIT = 3, //quit + ILL = 4, //illegal instruction + TRAP = 5, //trace trap (not reset when caught) + ABRT = 6, //SIGIOT + EMT = 7, //emulation trap + FPE = 8, //floating point exception + KILL = 9, //kill + BUS = 10, //bus error + SEGV = 11, //segmentation violation + SYS = 12, //invalid system call + PIPE = 13, //write to pipe with reading end closed + ALRM = 14, //alarm + TERM = 15, //termination requested + URG = 16, //urgent data on socket + STOP = 17, //process stop + TSTP = 18, //tty stop + CONT = 19, //process continue + CHLD = 20, //SIGCLD + TTIN = 21, //background tty read + TTOU = 22, //background tty write + IO = 23, //input/output ready/Pollable event + XCPU = 24, //CPU resource exceeded + XFSZ = 25, //file size limit exceeded + VTALRM = 26, //virtual time alarm + PROF = 27, //profiling time alarm + WINCH = 28, //window size changes + LOST = 29, //resource lost + USR1 = 30, //user defined signal 1 + USR2 = 31, //user defined signal 2 + PWR = 32, //power failure + POLL = 33, //pollable event + WIND = 34, //SIGWIND + PHONE = 35, //SIGPHONE + WAITING = 36, //process's LWPs are blocked + LWP = 37, //signal LWP + DANGER = 38, //swap space dangerously low + GRANT = 39, //monitor mode granted + RETRACT = 40, //need to relinquish monitor mode + MSG = 41, //monitor mode data available + SOUND = 42, //sound completed + SAK = 43, //secure attention + PRIO = 44, //SIGPRIO + + SIG33 = 45, //real-time event 33 + SIG34 = 46, //real-time event 34 + SIG35 = 47, //real-time event 35 + SIG36 = 48, //real-time event 36 + SIG37 = 49, //real-time event 37 + SIG38 = 50, //real-time event 38 + SIG39 = 51, //real-time event 39 + SIG40 = 52, //real-time event 40 + SIG41 = 53, //real-time event 41 + SIG42 = 54, //real-time event 42 + SIG43 = 55, //real-time event 43 + SIG44 = 56, //real-time event 44 + SIG45 = 57, //real-time event 45 + SIG46 = 58, //real-time event 46 + SIG47 = 59, //real-time event 47 + SIG48 = 60, //real-time event 48 + SIG49 = 61, //real-time event 49 + SIG50 = 62, //real-time event 50 + SIG51 = 63, //real-time event 51 + SIG52 = 64, //real-time event 52 + SIG53 = 65, //real-time event 53 + SIG54 = 66, //real-time event 54 + SIG55 = 67, //real-time event 55 + SIG56 = 68, //real-time event 56 + SIG57 = 69, //real-time event 57 + SIG58 = 70, //real-time event 58 + SIG59 = 71, //real-time event 59 + SIG60 = 72, //real-time event 60 + SIG61 = 73, //real-time event 61 + SIG62 = 74, //real-time event 62 + SIG63 = 75, //real-time event 63 + + CANCEL = 76, //LWP internal signal + + SIG32 = 77, //real-time event 32 + SIG64 = 78, //real-time event 64 + SIG65 = 79, //real-time event 65 + SIG66 = 80, //real-time event 66 + SIG67 = 81, //real-time event 67 + SIG68 = 82, //real-time event 68 + SIG69 = 83, //real-time event 69 + SIG70 = 84, //real-time event 70 + SIG71 = 85, //real-time event 71 + SIG72 = 86, //real-time event 72 + SIG73 = 87, //real-time event 73 + SIG74 = 88, //real-time event 74 + SIG75 = 89, //real-time event 75 + SIG76 = 90, //real-time event 76 + SIG77 = 91, //real-time event 77 + SIG78 = 92, //real-time event 78 + SIG79 = 93, //real-time event 79 + SIG80 = 94, //real-time event 80 + SIG81 = 95, //real-time event 81 + SIG82 = 96, //real-time event 82 + SIG83 = 97, //real-time event 83 + SIG84 = 98, //real-time event 84 + SIG85 = 99, //real-time event 85 + SIG86 = 100, //real-time event 86 + SIG87 = 101, //real-time event 87 + SIG88 = 102, //real-time event 88 + SIG89 = 103, //real-time event 89 + SIG90 = 104, //real-time event 90 + SIG91 = 105, //real-time event 91 + SIG92 = 106, //real-time event 92 + SIG93 = 107, //real-time event 93 + SIG94 = 108, //real-time event 94 + SIG95 = 109, //real-time event 95 + SIG96 = 110, //real-time event 96 + SIG97 = 111, //real-time event 97 + SIG98 = 112, //real-time event 98 + SIG99 = 113, //real-time event 99 + SIG100 = 114, //real-time event 100 + SIG101 = 115, //real-time event 101 + SIG102 = 116, //real-time event 102 + SIG103 = 117, //real-time event 103 + SIG104 = 118, //real-time event 104 + SIG105 = 119, //real-time event 105 + SIG106 = 120, //real-time event 106 + SIG107 = 121, //real-time event 107 + SIG108 = 122, //real-time event 108 + SIG109 = 123, //real-time event 109 + SIG110 = 124, //real-time event 110 + SIG111 = 125, //real-time event 111 + SIG112 = 126, //real-time event 112 + SIG113 = 127, //real-time event 113 + SIG114 = 128, //real-time event 114 + SIG115 = 129, //real-time event 115 + SIG116 = 130, //real-time event 116 + SIG117 = 131, //real-time event 117 + SIG118 = 132, //real-time event 118 + SIG119 = 133, //real-time event 119 + SIG120 = 134, //real-time event 120 + SIG121 = 135, //real-time event 121 + SIG122 = 136, //real-time event 122 + SIG123 = 137, //real-time event 123 + SIG124 = 138, //real-time event 124 + SIG125 = 139, //real-time event 125 + SIG126 = 140, //real-time event 126 + SIG127 = 141, //real-time event 127 + + INFO = 142, //information request + unknown = 143, //unknown signal + + EXC_BAD_ACCESS = 145, //could not access memory + EXC_BAD_INSTRUCTION = 146, //illegal instruction/operand + EXC_ARITHMETIC = 147, //arithmetic exception + EXC_EMULATION = 148, //emulation instruction + EXC_SOFTWARE = 149, //software generated exception + EXC_BREAKPOINT = 150, //breakpoint + + LIBRT = 151, //librt internal signal + }; +} +#endif /* __BASE_GDB_SIGNALS_HH__ */ diff --git a/ext/iostream3/SConscript b/ext/iostream3/SConscript index df0b2132f2..3b4e93701d 100644 --- a/ext/iostream3/SConscript +++ b/ext/iostream3/SConscript @@ -41,6 +41,6 @@ Import('env') env.Library('iostream3', [env.SharedObject('zfstream.cc')]) -env.Prepend(CPPPATH=Dir('.')) +env.Prepend(CPPPATH=Dir('.').srcnode()) env.Append(LIBS=['iostream3']) env.Prepend(LIBPATH=[Dir('.')]) diff --git a/ext/libelf/SConscript b/ext/libelf/SConscript index 535e216ddf..d6f8234bda 100644 --- a/ext/libelf/SConscript +++ b/ext/libelf/SConscript @@ -127,16 +127,19 @@ if not SCons.Tool.m4.exists(m4env): # Setup m4 tool m4env.Tool('m4') -m4env.Append(M4FLAGS=['-DSRCDIR=%s' % Dir('.').path]) +m4env.Append(M4FLAGS=['-DSRCDIR=%s' % Dir('.').srcnode().path]) m4env['M4COM'] = '$M4 $M4FLAGS $SOURCES > $TARGET' m4env.M4(target=File('libelf_convert.c'), - source=[File('elf_types.m4'), File('libelf_convert.m4')]) + source=[File('elf_types.m4').srcnode(), + File('libelf_convert.m4').srcnode()]) m4env.M4(target=File('libelf_fsize.c'), - source=[File('elf_types.m4'), File('libelf_fsize.m4')]) + source=[File('elf_types.m4').srcnode(), + File('libelf_fsize.m4').srcnode()]) m4env.M4(target=File('libelf_msize.c'), - source=[File('elf_types.m4'), File('libelf_msize.m4')]) + source=[File('elf_types.m4').srcnode(), + File('libelf_msize.m4').srcnode()]) -m4env.Append(CPPPATH=Dir('.')) +m4env.Append(CPPPATH=[Dir('.'), Dir('.').srcnode()]) # Build libelf as a static library with PIC code so it can be linked # into either m5 or the library @@ -146,6 +149,6 @@ m4env.Library('elf', [m4env.SharedObject(f) for f in elf_files]) m4env.Command(File('native-elf-format.h'), File('native-elf-format'), '${SOURCE} > ${TARGET}') -env.Prepend(CPPPATH=Dir('.')) +env.Prepend(CPPPATH=Dir('.').srcnode()) env.Append(LIBS=[File('libelf.a')]) env.Prepend(LIBPATH=[Dir('.')]) diff --git a/ext/libfdt/SConscript b/ext/libfdt/SConscript index 64573b78c8..a509bbebad 100644 --- a/ext/libfdt/SConscript +++ b/ext/libfdt/SConscript @@ -44,6 +44,6 @@ FdtFile('fdt_empty_tree.c') FdtFile('fdt_strerror.c') env.Library('fdt', [env.SharedObject(f) for f in fdt_files]) -env.Prepend(CPPPATH=Dir('.')) +env.Prepend(CPPPATH=Dir('.').srcnode()) env.Append(LIBS=['fdt']) env.Prepend(LIBPATH=[Dir('.')]) diff --git a/ext/nomali/SConscript b/ext/nomali/SConscript index b156ab0b3e..bcc5cfbd7b 100644 --- a/ext/nomali/SConscript +++ b/ext/nomali/SConscript @@ -39,7 +39,7 @@ Import('env') -env.Prepend(CPPPATH=Dir('./include')) +env.Prepend(CPPPATH=Dir('include').srcnode()) nomali = env.Clone() nomali.Append(CCFLAGS=['-Wno-ignored-qualifiers']) diff --git a/ext/pybind11/.appveyor.yml b/ext/pybind11/.appveyor.yml index 85445d41a2..360760ac8d 100644 --- a/ext/pybind11/.appveyor.yml +++ b/ext/pybind11/.appveyor.yml @@ -1,6 +1,6 @@ version: 1.0.{build} image: -- Visual Studio 2015 +- Visual Studio 2017 test: off skip_branch_with_pr: true build: @@ -11,11 +11,9 @@ environment: matrix: - PYTHON: 36 CONFIG: Debug - - PYTHON: 27 - CONFIG: Debug install: - ps: | - $env:CMAKE_GENERATOR = "Visual Studio 14 2015" + $env:CMAKE_GENERATOR = "Visual Studio 15 2017" if ($env:PLATFORM -eq "x64") { $env:PYTHON = "$env:PYTHON-x64" } $env:PATH = "C:\Python$env:PYTHON\;C:\Python$env:PYTHON\Scripts\;$env:PATH" python -W ignore -m pip install --upgrade pip wheel diff --git a/ext/pybind11/.clang-format b/ext/pybind11/.clang-format index 8700fca84d..b477a16037 100644 --- a/ext/pybind11/.clang-format +++ b/ext/pybind11/.clang-format @@ -3,19 +3,36 @@ # clang-format --style=llvm --dump-config BasedOnStyle: LLVM AccessModifierOffset: -4 -AlignConsecutiveAssignments: true +AllowShortLambdasOnASingleLine: true AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BreakBeforeBinaryOperators: All BreakConstructorInitializers: BeforeColon ColumnLimit: 99 +CommentPragmas: 'NOLINT:.*|^ IWYU pragma:' +IncludeBlocks: Regroup IndentCaseLabels: true IndentPPDirectives: AfterHash IndentWidth: 4 Language: Cpp SpaceAfterCStyleCast: true -# SpaceInEmptyBlock: true # too new Standard: Cpp11 +StatementMacros: ['PyObject_HEAD'] TabWidth: 4 +IncludeCategories: + - Regex: '' + Priority: 4 + - Regex: '.*' + Priority: 5 ... diff --git a/ext/pybind11/.clang-tidy b/ext/pybind11/.clang-tidy index e29d929897..23018386c1 100644 --- a/ext/pybind11/.clang-tidy +++ b/ext/pybind11/.clang-tidy @@ -1,13 +1,77 @@ FormatStyle: file -Checks: ' -llvm-namespace-comment, -modernize-use-override, -readability-container-size-empty, -modernize-use-using, -modernize-use-equals-default, -modernize-use-auto, -modernize-use-emplace, -' +Checks: | + *bugprone*, + *performance*, + clang-analyzer-optin.cplusplus.VirtualCall, + clang-analyzer-optin.performance.Padding, + cppcoreguidelines-init-variables, + cppcoreguidelines-prefer-member-initializer, + cppcoreguidelines-pro-type-static-cast-downcast, + cppcoreguidelines-slicing, + google-explicit-constructor, + llvm-namespace-comment, + misc-definitions-in-headers, + misc-misplaced-const, + misc-non-copyable-objects, + misc-static-assert, + misc-throw-by-value-catch-by-reference, + misc-uniqueptr-reset-release, + misc-unused-parameters, + modernize-avoid-bind, + modernize-loop-convert, + modernize-make-shared, + modernize-redundant-void-arg, + modernize-replace-auto-ptr, + modernize-replace-disallow-copy-and-assign-macro, + modernize-replace-random-shuffle, + modernize-shrink-to-fit, + modernize-use-auto, + modernize-use-bool-literals, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-use-equals-default, + modernize-use-equals-delete, + modernize-use-noexcept, + modernize-use-nullptr, + modernize-use-override, + modernize-use-using, + readability-avoid-const-params-in-decls, + readability-braces-around-statements, + readability-const-return-type, + readability-container-size-empty, + readability-delete-null-pointer, + readability-else-after-return, + readability-implicit-bool-conversion, + readability-inconsistent-declaration-parameter-name, + readability-make-member-function-const, + readability-misplaced-array-index, + readability-non-const-parameter, + readability-qualified-auto, + readability-redundant-function-ptr-dereference, + readability-redundant-smartptr-get, + readability-redundant-string-cstr, + readability-simplify-subscript-expr, + readability-static-accessed-through-instance, + readability-static-definition-in-anonymous-namespace, + readability-string-compare, + readability-suspicious-call-argument, + readability-uniqueptr-delete-release, + -bugprone-easily-swappable-parameters, + -bugprone-exception-escape, + -bugprone-reserved-identifier, + -bugprone-unused-raii, + +CheckOptions: +- key: modernize-use-equals-default.IgnoreMacros + value: false +- key: performance-for-range-copy.WarnOnAllAutoCopies + value: true +- key: performance-inefficient-string-concatenation.StrictMode + value: true +- key: performance-unnecessary-value-param.AllowedTypes + value: 'exception_ptr$;' +- key: readability-implicit-bool-conversion.AllowPointerConditions + value: true HeaderFilterRegex: 'pybind11/.*h' diff --git a/ext/pybind11/.codespell-ignore-lines b/ext/pybind11/.codespell-ignore-lines new file mode 100644 index 0000000000..2a01d63ebb --- /dev/null +++ b/ext/pybind11/.codespell-ignore-lines @@ -0,0 +1,24 @@ +template + template + auto &this_ = static_cast(*this); + if (load_impl(temp, false)) { + ssize_t nd = 0; + auto trivial = broadcast(buffers, nd, shape); + auto ndim = (size_t) nd; + int nd; + ssize_t ndim() const { return detail::array_proxy(m_ptr)->nd; } + using op = op_impl; +template + template + class_ &def(const detail::op_ &op, const Extra &...extra) { + class_ &def_cast(const detail::op_ &op, const Extra &...extra) { +@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"]) +struct IntStruct { + explicit IntStruct(int v) : value(v){}; + ~IntStruct() { value = -value; } + IntStruct(const IntStruct &) = default; + IntStruct &operator=(const IntStruct &) = default; + py::class_(m, "IntStruct").def(py::init([](const int i) { return IntStruct(i); })); + py::implicitly_convertible(); + m.def("test", [](int expected, const IntStruct &in) { + [](int expected, const IntStruct &in) { diff --git a/ext/pybind11/.gitattributes b/ext/pybind11/.gitattributes new file mode 100644 index 0000000000..d611e1496d --- /dev/null +++ b/ext/pybind11/.gitattributes @@ -0,0 +1 @@ +docs/*.svg binary diff --git a/ext/pybind11/.github/CODEOWNERS b/ext/pybind11/.github/CODEOWNERS new file mode 100644 index 0000000000..4e2c66902e --- /dev/null +++ b/ext/pybind11/.github/CODEOWNERS @@ -0,0 +1,9 @@ +*.cmake @henryiii +CMakeLists.txt @henryiii +*.yml @henryiii +*.yaml @henryiii +/tools/ @henryiii +/pybind11/ @henryiii +noxfile.py @henryiii +.clang-format @henryiii +.clang-tidy @henryiii diff --git a/ext/pybind11/.github/CONTRIBUTING.md b/ext/pybind11/.github/CONTRIBUTING.md index 08d9e7cb93..00b1fea4cf 100644 --- a/ext/pybind11/.github/CONTRIBUTING.md +++ b/ext/pybind11/.github/CONTRIBUTING.md @@ -53,6 +53,33 @@ derivative works thereof, in binary and source code form. ## Development of pybind11 +### Quick setup + +To setup a quick development environment, use [`nox`](https://nox.thea.codes). +This will allow you to do some common tasks with minimal setup effort, but will +take more time to run and be less flexible than a full development environment. +If you use [`pipx run nox`](https://pipx.pypa.io), you don't even need to +install `nox`. Examples: + +```bash +# List all available sessions +nox -l + +# Run linters +nox -s lint + +# Run tests on Python 3.9 +nox -s tests-3.9 + +# Build and preview docs +nox -s docs -- serve + +# Build SDists and wheels +nox -s build +``` + +### Full setup + To setup an ideal development environment, run the following commands on a system with CMake 3.14+: @@ -66,11 +93,10 @@ cmake --build build -j4 Tips: -* You can use `virtualenv` (from PyPI) instead of `venv` (which is Python 3 - only). +* You can use `virtualenv` (faster, from PyPI) instead of `venv`. * You can select any name for your environment folder; if it contains "env" it will be ignored by git. -* If you don’t have CMake 3.14+, just add “cmake” to the pip install command. +* If you don't have CMake 3.14+, just add "cmake" to the pip install command. * You can use `-DPYBIND11_FINDPYTHON=ON` to use FindPython on CMake 3.12+ * In classic mode, you may need to set `-DPYTHON_EXECUTABLE=/path/to/python`. FindPython uses `-DPython_ROOT_DIR=/path/to` or @@ -78,7 +104,7 @@ Tips: ### Configuration options -In CMake, configuration options are given with “-D”. Options are stored in the +In CMake, configuration options are given with "-D". Options are stored in the build directory, in the `CMakeCache.txt` file, so they are remembered for each build directory. Two selections are special - the generator, given with `-G`, and the compiler, which is selected based on environment variables `CXX` and @@ -88,12 +114,12 @@ after the initial run. The valid options are: * `-DCMAKE_BUILD_TYPE`: Release, Debug, MinSizeRel, RelWithDebInfo -* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+’s FindPython instead of the +* `-DPYBIND11_FINDPYTHON=ON`: Use CMake 3.12+'s FindPython instead of the classic, deprecated, custom FindPythonLibs * `-DPYBIND11_NOPYTHON=ON`: Disable all Python searching (disables tests) * `-DBUILD_TESTING=ON`: Enable the tests * `-DDOWNLOAD_CATCH=ON`: Download catch to build the C++ tests -* `-DOWNLOAD_EIGEN=ON`: Download Eigen for the NumPy tests +* `-DDOWNLOAD_EIGEN=ON`: Download Eigen for the NumPy tests * `-DPYBIND11_INSTALL=ON/OFF`: Enable the install target (on by default for the master project) * `-DUSE_PYTHON_INSTALL_DIR=ON`: Try to install into the python dir @@ -132,8 +158,9 @@ tests with these targets: * `test_cmake_build`: Install / subdirectory tests If you want to build just a subset of tests, use -`-DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp"`. If this is -empty, all tests will be built. +`-DPYBIND11_TEST_OVERRIDE="test_callbacks;test_pickling"`. If this is +empty, all tests will be built. Tests are specified without an extension if they need both a .py and +.cpp file. You may also pass flags to the `pytest` target by editing `tests/pytest.ini` or by using the `PYTEST_ADDOPTS` environment variable @@ -203,16 +230,19 @@ of the pybind11 repo. [`clang-tidy`][clang-tidy] performs deeper static code analyses and is more complex to run, compared to `clang-format`, but support for `clang-tidy` is built into the pybind11 CMake configuration. To run `clang-tidy`, the -following recipe should work. Files will be modified in place, so you can -use git to monitor the changes. +following recipe should work. Run the `docker` command from the top-level +directory inside your pybind11 git clone. Files will be modified in place, +so you can use git to monitor the changes. ```bash -docker run --rm -v $PWD:/pybind11 -it silkeh/clang:10 -apt-get update && apt-get install python3-dev python3-pytest -cmake -S pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);-fix" -cmake --build build +docker run --rm -v $PWD:/mounted_pybind11 -it silkeh/clang:13 +apt-get update && apt-get install -y python3-dev python3-pytest +cmake -S /mounted_pybind11/ -B build -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17 +cmake --build build -j 2 ``` +You can add `--fix` to the options list if you want. + ### Include what you use To run include what you use, install (`brew install include-what-you-use` on @@ -228,7 +258,7 @@ The report is sent to stderr; you can pipe it into a file if you wish. ### Build recipes This builds with the Intel compiler (assuming it is in your path, along with a -recent CMake and Python 3): +recent CMake and Python): ```bash python3 -m venv venv diff --git a/ext/pybind11/.github/ISSUE_TEMPLATE/bug-report.md b/ext/pybind11/.github/ISSUE_TEMPLATE/bug-report.md deleted file mode 100644 index ae36ea6508..0000000000 --- a/ext/pybind11/.github/ISSUE_TEMPLATE/bug-report.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: Bug Report -about: File an issue about a bug -title: "[BUG] " ---- - - -Make sure you've completed the following steps before submitting your issue -- thank you! - -1. Make sure you've read the [documentation][]. Your issue may be addressed there. -2. Search the [issue tracker][] to verify that this hasn't already been reported. +1 or comment there if it has. -3. Consider asking first in the [Gitter chat room][]. -4. Include a self-contained and minimal piece of code that reproduces the problem. If that's not possible, try to make the description as clear as possible. - a. If possible, make a PR with a new, failing test to give us a starting point to work on! - -[documentation]: https://pybind11.readthedocs.io -[issue tracker]: https://github.com/pybind/pybind11/issues -[Gitter chat room]: https://gitter.im/pybind/Lobby - -*After reading, remove this checklist and the template text in parentheses below.* - -## Issue description - -(Provide a short description, state the expected behavior and what actually happens.) - -## Reproducible example code - -(The code should be minimal, have no external dependencies, isolate the function(s) that cause breakage. Submit matched and complete C++ and Python snippets that can be easily compiled and run to diagnose the issue.) diff --git a/ext/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml b/ext/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml new file mode 100644 index 0000000000..4f1e78f33c --- /dev/null +++ b/ext/pybind11/.github/ISSUE_TEMPLATE/bug-report.yml @@ -0,0 +1,61 @@ +name: Bug Report +description: File an issue about a bug +title: "[BUG]: " +labels: [triage] +body: + - type: markdown + attributes: + value: | + Please do your best to make the issue as easy to act on as possible, and only submit here if there is clearly a problem with pybind11 (ask first if unsure). **Note that a reproducer in a PR is much more likely to get immediate attention.** + + - type: checkboxes + id: steps + attributes: + label: Required prerequisites + description: Make sure you've completed the following steps before submitting your issue -- thank you! + options: + - label: Make sure you've read the [documentation](https://pybind11.readthedocs.io). Your issue may be addressed there. + required: true + - label: Search the [issue tracker](https://github.com/pybind/pybind11/issues) and [Discussions](https:/pybind/pybind11/discussions) to verify that this hasn't already been reported. +1 or comment there if it has. + required: true + - label: Consider asking first in the [Gitter chat room](https://gitter.im/pybind/Lobby) or in a [Discussion](https:/pybind/pybind11/discussions/new). + required: false + + - type: input + id: version + attributes: + label: What version (or hash if on master) of pybind11 are you using? + validations: + required: true + + - type: textarea + id: description + attributes: + label: Problem description + placeholder: >- + Provide a short description, state the expected behavior and what + actually happens. Include relevant information like what version of + pybind11 you are using, what system you are on, and any useful commands + / output. + validations: + required: true + + - type: textarea + id: code + attributes: + label: Reproducible example code + placeholder: >- + The code should be minimal, have no external dependencies, isolate the + function(s) that cause breakage. Submit matched and complete C++ and + Python snippets that can be easily compiled and run to diagnose the + issue. — Note that a reproducer in a PR is much more likely to get + immediate attention: failing tests in the pybind11 CI are the best + starting point for working out fixes. + render: text + + - type: input + id: regression + attributes: + label: Is this a regression? Put the last known working version here if it is. + description: Put the last known working version here if this is a regression. + value: Not a regression diff --git a/ext/pybind11/.github/ISSUE_TEMPLATE/config.yml b/ext/pybind11/.github/ISSUE_TEMPLATE/config.yml index 20e743136f..27f9a80441 100644 --- a/ext/pybind11/.github/ISSUE_TEMPLATE/config.yml +++ b/ext/pybind11/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: + - name: Ask a question + url: https://github.com/pybind/pybind11/discussions/new + about: Please ask and answer questions here, or propose new ideas. - name: Gitter room url: https://gitter.im/pybind/Lobby about: A room for discussing pybind11 with an active community diff --git a/ext/pybind11/.github/ISSUE_TEMPLATE/feature-request.md b/ext/pybind11/.github/ISSUE_TEMPLATE/feature-request.md deleted file mode 100644 index 5f6ec81ec9..0000000000 --- a/ext/pybind11/.github/ISSUE_TEMPLATE/feature-request.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: Feature Request -about: File an issue about adding a feature -title: "[FEAT] " ---- - - -Make sure you've completed the following steps before submitting your issue -- thank you! - -1. Check if your feature has already been mentioned / rejected / planned in other issues. -2. If those resources didn't help, consider asking in the [Gitter chat room][] to see if this is interesting / useful to a larger audience and possible to implement reasonably, -4. If you have a useful feature that passes the previous items (or not suitable for chat), please fill in the details below. - -[Gitter chat room]: https://gitter.im/pybind/Lobby - -*After reading, remove this checklist.* diff --git a/ext/pybind11/.github/ISSUE_TEMPLATE/question.md b/ext/pybind11/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index b199b6ee8a..0000000000 --- a/ext/pybind11/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -name: Question -about: File an issue about unexplained behavior -title: "[QUESTION] " ---- - -If you have a question, please check the following first: - -1. Check if your question has already been answered in the [FAQ][] section. -2. Make sure you've read the [documentation][]. Your issue may be addressed there. -3. If those resources didn't help and you only have a short question (not a bug report), consider asking in the [Gitter chat room][] -4. Search the [issue tracker][], including the closed issues, to see if your question has already been asked/answered. +1 or comment if it has been asked but has no answer. -5. If you have a more complex question which is not answered in the previous items (or not suitable for chat), please fill in the details below. -6. Include a self-contained and minimal piece of code that illustrates your question. If that's not possible, try to make the description as clear as possible. - -[FAQ]: http://pybind11.readthedocs.io/en/latest/faq.html -[documentation]: https://pybind11.readthedocs.io -[issue tracker]: https://github.com/pybind/pybind11/issues -[Gitter chat room]: https://gitter.im/pybind/Lobby - -*After reading, remove this checklist.* diff --git a/ext/pybind11/.github/dependabot.yml b/ext/pybind11/.github/dependabot.yml index 73273365c0..2c7d170839 100644 --- a/ext/pybind11/.github/dependabot.yml +++ b/ext/pybind11/.github/dependabot.yml @@ -5,12 +5,3 @@ updates: directory: "/" schedule: interval: "daily" - ignore: - # Official actions have moving tags like v1 - # that are used, so they don't need updates here - - dependency-name: "actions/checkout" - - dependency-name: "actions/setup-python" - - dependency-name: "actions/cache" - - dependency-name: "actions/upload-artifact" - - dependency-name: "actions/download-artifact" - - dependency-name: "actions/labeler" diff --git a/ext/pybind11/.github/matchers/pylint.json b/ext/pybind11/.github/matchers/pylint.json new file mode 100644 index 0000000000..e3a6bd16b0 --- /dev/null +++ b/ext/pybind11/.github/matchers/pylint.json @@ -0,0 +1,32 @@ +{ + "problemMatcher": [ + { + "severity": "warning", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):(\\d+): ([A-DF-Z]\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ], + "owner": "pylint-warning" + }, + { + "severity": "error", + "pattern": [ + { + "regexp": "^([^:]+):(\\d+):(\\d+): (E\\d+): \\033\\[[\\d;]+m([^\\033]+).*$", + "file": 1, + "line": 2, + "column": 3, + "code": 4, + "message": 5 + } + ], + "owner": "pylint-error" + } + ] +} diff --git a/ext/pybind11/.github/pull_request_template.md b/ext/pybind11/.github/pull_request_template.md index 97a6ff7dda..54b7f5100d 100644 --- a/ext/pybind11/.github/pull_request_template.md +++ b/ext/pybind11/.github/pull_request_template.md @@ -1,3 +1,7 @@ + ## Description diff --git a/ext/pybind11/.github/workflows/ci.yml b/ext/pybind11/.github/workflows/ci.yml index f90c199526..b36bbfe1b9 100644 --- a/ext/pybind11/.github/workflows/ci.yml +++ b/ext/pybind11/.github/workflows/ci.yml @@ -9,6 +9,17 @@ on: - stable - v* +concurrency: + group: test-${{ github.ref }} + cancel-in-progress: true + +env: + PIP_ONLY_BINARY: numpy + FORCE_COLOR: 3 + PYTEST_TIMEOUT: 300 + # For cmake: + VERBOSE: 1 + jobs: # This is the "main" test suite, which tests a large number of different # versions of default compilers and Python versions in GitHub Actions. @@ -16,66 +27,66 @@ jobs: strategy: fail-fast: false matrix: - runs-on: [ubuntu-latest, windows-latest, macos-latest] + runs-on: [ubuntu-20.04, windows-2022, macos-latest] python: - - 2.7 - - 3.5 - - 3.6 - - 3.9 - # - 3.10-dev # Re-enable once 3.10.0a5 is released - - pypy2 - - pypy3 + - '3.6' + - '3.9' + - '3.10' + - '3.11' + - 'pypy-3.7' + - 'pypy-3.8' + - 'pypy-3.9' # Items in here will either be added to the build matrix (if not # present), or add new keys to an existing matrix element if all the # existing keys match. # - # We support three optional keys: args (both build), args1 (first - # build), and args2 (second build). + # We support an optional key: args, for cmake args include: # Just add a key - - runs-on: ubuntu-latest - python: 3.6 + - runs-on: ubuntu-20.04 + python: '3.6' args: > -DPYBIND11_FINDPYTHON=ON - - runs-on: windows-latest - python: 3.6 + -DCMAKE_CXX_FLAGS="-D_=1" + - runs-on: ubuntu-20.04 + python: 'pypy-3.8' args: > -DPYBIND11_FINDPYTHON=ON - - # These items will be removed from the build matrix, keys must match. - exclude: - # Currently 32bit only, and we build 64bit - - runs-on: windows-latest - python: pypy2 - - runs-on: windows-latest - python: pypy3 - - # TODO: PyPy2 7.3.3 segfaults, while 7.3.2 was fine. - - runs-on: ubuntu-latest - python: pypy2 + - runs-on: windows-2019 + python: '3.6' + args: > + -DPYBIND11_FINDPYTHON=ON + # Inject a couple Windows 2019 runs + - runs-on: windows-2019 + python: '3.9' name: "🐍 ${{ matrix.python }} • ${{ matrix.runs-on }} • x64 ${{ matrix.args }}" runs-on: ${{ matrix.runs-on }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} - - name: Setup Boost (Windows / Linux latest) - shell: bash - run: echo "BOOST_ROOT=$BOOST_ROOT_1_72_0" >> $GITHUB_ENV + - name: Setup Boost (Linux) + # Can't use boost + define _ + if: runner.os == 'Linux' && matrix.python != '3.6' + run: sudo apt-get install libboost-dev + + - name: Setup Boost (macOS) + if: runner.os == 'macOS' + run: brew install boost - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.7 + uses: jwlawson/actions-setup-cmake@v1.13 - name: Cache wheels if: runner.os == 'macOS' - uses: actions/cache@v2 + uses: actions/cache@v3 with: # This path is specific to macOS - we really only need it for PyPy NumPy wheels # See https://github.com/actions/cache/blob/master/examples.md#python---pip @@ -85,17 +96,20 @@ jobs: key: ${{ runner.os }}-pip-${{ matrix.python }}-x64-${{ hashFiles('tests/requirements.txt') }} - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Setup annotations on Linux if: runner.os == 'Linux' run: python -m pip install pytest-github-actions-annotate-failures # First build - C++11 mode and inplace + # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON here. - name: Configure C++11 ${{ matrix.args }} run: > cmake -S . -B . -DPYBIND11_WERROR=ON + -DPYBIND11_SIMPLE_GIL_MANAGEMENT=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=11 @@ -109,7 +123,7 @@ jobs: - name: C++11 tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))" run: cmake --build . --target cpptest -j 2 - name: Interface test C++11 @@ -119,15 +133,16 @@ jobs: run: git clean -fdx # Second build - C++17 mode and in a build directory - - name: Configure ${{ matrix.args2 }} + # More-or-less randomly adding -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF here. + - name: Configure C++17 run: > cmake -S . -B build2 -DPYBIND11_WERROR=ON + -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON -DCMAKE_CXX_STANDARD=17 ${{ matrix.args }} - ${{ matrix.args2 }} - name: Build run: cmake --build build2 -j 2 @@ -137,32 +152,35 @@ jobs: - name: C++ tests # TODO: Figure out how to load the DLL on Python 3.8+ - if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10-dev'))" + if: "!(runner.os == 'Windows' && (matrix.python == 3.8 || matrix.python == 3.9 || matrix.python == '3.10' || matrix.python == '3.11' || matrix.python == 'pypy-3.8'))" run: cmake --build build2 --target cpptest + # Third build - C++17 mode with unstable ABI + - name: Configure (unstable ABI) + run: > + cmake -S . -B build3 + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + -DPYBIND11_INTERNALS_VERSION=10000000 + "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" + ${{ matrix.args }} + + - name: Build (unstable ABI) + run: cmake --build build3 -j 2 + + - name: Python tests (unstable ABI) + run: cmake --build build3 --target pytest + - name: Interface test run: cmake --build build2 --target test_cmake_build - # Eventually Microsoft might have an action for setting up - # MSVC, but for now, this action works: - - name: Prepare compiler environment for Windows 🐍 2.7 - if: matrix.python == 2.7 && runner.os == 'Windows' - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - # This makes two environment variables available in the following step(s) - - name: Set Windows 🐍 2.7 environment variables - if: matrix.python == 2.7 && runner.os == 'Windows' - shell: bash - run: | - echo "DISTUTILS_USE_SDK=1" >> $GITHUB_ENV - echo "MSSdk=1" >> $GITHUB_ENV - # This makes sure the setup_helpers module can build packages using # setuptools - name: Setuptools helpers test run: pytest tests/extra_setuptools + if: "!(matrix.runs-on == 'windows-2022')" deadsnakes: @@ -170,30 +188,31 @@ jobs: fail-fast: false matrix: include: - - python-version: 3.9 + # TODO: Fails on 3.10, investigate + - python-version: "3.9" python-debug: true valgrind: true - - python-version: 3.10-dev + - python-version: "3.11" python-debug: false name: "🐍 ${{ matrix.python-version }}${{ matrix.python-debug && '-dbg' || '' }} (deadsnakes)${{ matrix.valgrind && ' • Valgrind' || '' }} • x64" - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python-version }} (deadsnakes) - uses: deadsnakes/action@v2.1.1 + uses: deadsnakes/action@v3.0.0 with: python-version: ${{ matrix.python-version }} debug: ${{ matrix.python-debug }} - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.7 + uses: jwlawson/actions-setup-cmake@v1.13 - name: Valgrind cache if: matrix.valgrind - uses: actions/cache@v2 + uses: actions/cache@v3 id: cache-valgrind with: path: valgrind @@ -218,9 +237,12 @@ jobs: sudo apt-get install libc6-dbg # Needed by Valgrind - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Configure + env: + SETUPTOOLS_USE_DISTUTILS: stdlib run: > cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug @@ -261,16 +283,22 @@ jobs: include: - clang: 5 std: 14 - - clang: 10 - std: 20 - clang: 10 std: 17 + - clang: 11 + std: 20 + - clang: 12 + std: 20 + - clang: 13 + std: 20 + - clang: 14 + std: 20 name: "🐍 3 • Clang ${{ matrix.clang }} • C++${{ matrix.std }} • x64" container: "silkeh/clang:${{ matrix.clang }}" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Add wget and python3 run: apt-get update && apt-get install -y python3-dev python3-numpy python3-pytest libeigen3-dev @@ -300,11 +328,11 @@ jobs: # Testing NVCC; forces sources to behave like .cu files cuda: runs-on: ubuntu-latest - name: "🐍 3.8 • CUDA 11 • Ubuntu 20.04" - container: nvidia/cuda:11.0-devel-ubuntu20.04 + name: "🐍 3.10 • CUDA 11.7 • Ubuntu 22.04" + container: nvidia/cuda:11.7.0-devel-ubuntu22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # tzdata will try to ask for the timezone, so set the DEBIAN_FRONTEND - name: Install 🐍 3 @@ -328,7 +356,7 @@ jobs: # container: centos:8 # # steps: -# - uses: actions/checkout@v2 +# - uses: actions/checkout@v3 # # - name: Add Python 3 and a few requirements # run: yum update -y && yum install -y git python3-devel python3-numpy python3-pytest make environment-modules @@ -367,32 +395,32 @@ jobs: # Testing on CentOS 7 + PGI compilers, which seems to require more workarounds centos-nvhpc7: runs-on: ubuntu-latest - name: "🐍 3 • CentOS7 / PGI 20.9 • x64" + name: "🐍 3 • CentOS7 / PGI 22.9 • x64" container: centos:7 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Add Python 3 and a few requirements - run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 + run: yum update -y && yum install -y epel-release && yum install -y git python3-devel make environment-modules cmake3 yum-utils - name: Install NVidia HPC SDK - run: yum -y install https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-20-9-20.9-1.x86_64.rpm https://developer.download.nvidia.com/hpc-sdk/20.9/nvhpc-2020-20.9-1.x86_64.rpm + run: yum-config-manager --add-repo https://developer.download.nvidia.com/hpc-sdk/rhel/nvhpc.repo && yum -y install nvhpc-22.9 # On CentOS 7, we have to filter a few tests (compiler internal error) - # and allow deeper templete recursion (not needed on CentOS 8 with a newer + # and allow deeper template recursion (not needed on CentOS 8 with a newer # standard library). On some systems, you many need further workarounds: # https://github.com/pybind/pybind11/pull/2475 - name: Configure shell: bash run: | source /etc/profile.d/modules.sh - module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/20.9 + module load /opt/nvidia/hpc_sdk/modulefiles/nvhpc/22.9 cmake3 -S . -B build -DDOWNLOAD_CATCH=ON \ -DCMAKE_CXX_STANDARD=11 \ -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") \ -DCMAKE_CXX_FLAGS="-Wc,--pending_instantiations=0" \ - -DPYBIND11_TEST_FILTER="test_smart_ptr.cpp;test_virtual_functions.cpp" + -DPYBIND11_TEST_FILTER="test_smart_ptr.cpp" # Building before installing Pip should produce a warning but not an error - name: Build @@ -419,20 +447,20 @@ jobs: strategy: fail-fast: false matrix: - gcc: - - 7 - - latest - std: - - 11 include: - - gcc: 10 - std: 20 + - { gcc: 7, std: 11 } + - { gcc: 7, std: 17 } + - { gcc: 8, std: 14 } + - { gcc: 8, std: 17 } + - { gcc: 10, std: 17 } + - { gcc: 11, std: 20 } + - { gcc: 12, std: 20 } name: "🐍 3 • GCC ${{ matrix.gcc }} • C++${{ matrix.std }}• x64" container: "gcc:${{ matrix.gcc }}" steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Add Python 3 run: apt-get update; apt-get install -y python3-dev python3-numpy python3-pytest python3-pip libeigen3-dev @@ -441,7 +469,7 @@ jobs: run: python3 -m pip install --upgrade pip - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.7 + uses: jwlawson/actions-setup-cmake@v1.13 - name: Configure shell: bash @@ -474,7 +502,7 @@ jobs: name: "🐍 3 • ICC latest • x64" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Add apt repo run: | @@ -495,7 +523,7 @@ jobs: - name: Install dependencies run: | set +e; source /opt/intel/oneapi/setvars.sh; set -e - python3 -m pip install -r tests/requirements.txt --prefer-binary + python3 -m pip install -r tests/requirements.txt - name: Configure C++11 run: | @@ -569,29 +597,37 @@ jobs: strategy: fail-fast: false matrix: - centos: - - 7 # GCC 4.8 - - 8 + container: + - "centos:7" # GCC 4.8 + - "almalinux:8" + - "almalinux:9" - name: "🐍 3 • CentOS ${{ matrix.centos }} • x64" - container: "centos:${{ matrix.centos }}" + name: "🐍 3 • ${{ matrix.container }} • x64" + container: "${{ matrix.container }}" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Add Python 3 + - name: Add Python 3 (RHEL 7) + if: matrix.container == 'centos:7' run: yum update -y && yum install -y python3-devel gcc-c++ make git + - name: Add Python 3 (RHEL 8+) + if: matrix.container != 'centos:7' + run: dnf update -y && dnf install -y python3-devel gcc-c++ make git + - name: Update pip run: python3 -m pip install --upgrade pip - name: Install dependencies - run: python3 -m pip install cmake -r tests/requirements.txt --prefer-binary + run: | + python3 -m pip install cmake -r tests/requirements.txt - name: Configure shell: bash run: > cmake -S . -B build + -DCMAKE_BUILD_TYPE=MinSizeRel -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON @@ -613,18 +649,18 @@ jobs: # This tests an "install" with the CMake tools install-classic: - name: "🐍 3.5 • Debian • x86 • Install" + name: "🐍 3.7 • Debian • x86 • Install" runs-on: ubuntu-latest - container: i386/debian:stretch + container: i386/debian:buster steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v1 # Required to run inside docker - name: Install requirements run: | apt-get update apt-get install -y git make cmake g++ libeigen3-dev python3-dev python3-pip - pip3 install "pytest==3.1.*" + pip3 install "pytest==6.*" - name: Configure for install run: > @@ -649,33 +685,32 @@ jobs: -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") working-directory: /build-tests - - name: Run tests + - name: Python tests run: make pytest -j 2 working-directory: /build-tests # This verifies that the documentation is not horribly broken, and does a - # basic sanity check on the SDist. + # basic validation check on the SDist. doxygen: name: "Documentation build test" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" - name: Install Doxygen run: sudo apt-get install -y doxygen librsvg2-bin # Changed to rsvg-convert in 20.04 - - name: Install docs & setup requirements - run: python3 -m pip install -r docs/requirements.txt - - name: Build docs - run: python3 -m sphinx -W -b html docs docs/.build + run: pipx run nox -s docs - name: Make SDist - run: python3 setup.py sdist + run: pipx run nox -s build -- --sdist - run: git status --ignored @@ -687,7 +722,7 @@ jobs: - name: Compare Dists (headers only) working-directory: include run: | - python3 -m pip install --user -U ../dist/* + python3 -m pip install --user -U ../dist/*.tar.gz installed=$(python3 -c "import pybind11; print(pybind11.get_include() + '/pybind11')") diff -rq $installed ./pybind11 @@ -696,42 +731,43 @@ jobs: fail-fast: false matrix: python: - - 3.5 - 3.6 - 3.7 - 3.8 - 3.9 - - pypy3 - # TODO: fix hang on pypy2 include: - python: 3.9 - args: -DCMAKE_CXX_STANDARD=20 -DDOWNLOAD_EIGEN=OFF + args: -DCMAKE_CXX_STANDARD=20 - python: 3.8 args: -DCMAKE_CXX_STANDARD=17 + - python: 3.7 + args: -DCMAKE_CXX_STANDARD=14 + name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}" - runs-on: windows-latest + runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python ${{ matrix.python }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} architecture: x86 - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.7 + uses: jwlawson/actions-setup-cmake@v1.13 - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1 + uses: ilammy/msvc-dev-cmd@v1.12.0 with: arch: x86 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - name: Configure ${{ matrix.args }} @@ -745,102 +781,324 @@ jobs: - name: Build C++11 run: cmake --build build -j 2 - - name: Run tests + - name: Python tests run: cmake --build build -t pytest - win32-msvc2015: - name: "🐍 ${{ matrix.python }} • MSVC 2015 • x64" - runs-on: windows-latest + win32-debug: strategy: fail-fast: false matrix: python: - - 2.7 - - 3.6 - - 3.7 - # todo: check/cpptest does not support 3.8+ yet - - steps: - - uses: actions/checkout@v2 - - - name: Setup 🐍 ${{ matrix.python }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python }} - - - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.7 - - - name: Prepare MSVC - uses: ilammy/msvc-dev-cmd@v1 - with: - toolset: 14.0 - - - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary - - # First build - C++11 mode and inplace - - name: Configure - run: > - cmake -S . -B build - -G "Visual Studio 14 2015" -A x64 - -DPYBIND11_WERROR=ON - -DDOWNLOAD_CATCH=ON - -DDOWNLOAD_EIGEN=ON - - - name: Build C++14 - run: cmake --build build -j 2 - - - name: Run all checks - run: cmake --build build -t check - - - win32-msvc2017: - name: "🐍 ${{ matrix.python }} • MSVC 2017 • x64" - runs-on: windows-2016 - strategy: - fail-fast: false - matrix: - python: - - 2.7 - - 3.5 - - 3.7 - std: - - 14 + - 3.8 + - 3.9 include: - - python: 2.7 - std: 17 - args: > - -DCMAKE_CXX_FLAGS="/permissive- /EHsc /GR" + - python: 3.9 + args: -DCMAKE_CXX_STANDARD=20 + - python: 3.8 + args: -DCMAKE_CXX_STANDARD=17 + + name: "🐍 ${{ matrix.python }} • MSVC 2019 (Debug) • x86 ${{ matrix.args }}" + runs-on: windows-2019 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Setup 🐍 ${{ matrix.python }} - uses: actions/setup-python@v2 + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} + architecture: x86 - name: Update CMake - uses: jwlawson/actions-setup-cmake@v1.7 + uses: jwlawson/actions-setup-cmake@v1.13 + + - name: Prepare MSVC + uses: ilammy/msvc-dev-cmd@v1.12.0 + with: + arch: x86 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt # First build - C++11 mode and inplace - - name: Configure + - name: Configure ${{ matrix.args }} run: > cmake -S . -B build - -G "Visual Studio 15 2017" -A x64 + -G "Visual Studio 16 2019" -A Win32 + -DCMAKE_BUILD_TYPE=Debug -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -DDOWNLOAD_EIGEN=ON - -DCMAKE_CXX_STANDARD=${{ matrix.std }} ${{ matrix.args }} + - name: Build C++11 + run: cmake --build build --config Debug -j 2 - - name: Build ${{ matrix.std }} + - name: Python tests + run: cmake --build build --config Debug -t pytest + + + windows-2022: + strategy: + fail-fast: false + matrix: + python: + - 3.9 + + name: "🐍 ${{ matrix.python }} • MSVC 2022 C++20 • x64" + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v3 + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + + - name: Prepare env + run: | + python3 -m pip install -r tests/requirements.txt + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.13 + + - name: Configure C++20 + run: > + cmake -S . -B build + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=20 + + - name: Build C++20 run: cmake --build build -j 2 - - name: Run all checks - run: cmake --build build -t check + - name: Python tests + run: cmake --build build --target pytest + + - name: C++20 tests + run: cmake --build build --target cpptest -j 2 + + - name: Interface test C++20 + run: cmake --build build --target test_cmake_build + + mingw: + name: "🐍 3 • windows-latest • ${{ matrix.sys }}" + runs-on: windows-latest + defaults: + run: + shell: msys2 {0} + strategy: + fail-fast: false + matrix: + include: + - { sys: mingw64, env: x86_64 } + - { sys: mingw32, env: i686 } + steps: + - uses: msys2/setup-msys2@v2 + with: + msystem: ${{matrix.sys}} + install: >- + git + mingw-w64-${{matrix.env}}-gcc + mingw-w64-${{matrix.env}}-python-pip + mingw-w64-${{matrix.env}}-python-numpy + mingw-w64-${{matrix.env}}-python-scipy + mingw-w64-${{matrix.env}}-cmake + mingw-w64-${{matrix.env}}-make + mingw-w64-${{matrix.env}}-python-pytest + mingw-w64-${{matrix.env}}-eigen3 + mingw-w64-${{matrix.env}}-boost + mingw-w64-${{matrix.env}}-catch + + - uses: actions/checkout@v3 + + - name: Configure C++11 + # LTO leads to many undefined reference like + # `pybind11::detail::function_call::function_call(pybind11::detail::function_call&&) + run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=11 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build + + - name: Build C++11 + run: cmake --build build -j 2 + + - name: Python tests C++11 + run: cmake --build build --target pytest -j 2 + + - name: C++11 tests + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target cpptest -j 2 + + - name: Interface test C++11 + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build --target test_cmake_build + + - name: Clean directory + run: git clean -fdx + + - name: Configure C++14 + run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=14 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build2 + + - name: Build C++14 + run: cmake --build build2 -j 2 + + - name: Python tests C++14 + run: cmake --build build2 --target pytest -j 2 + + - name: C++14 tests + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target cpptest -j 2 + + - name: Interface test C++14 + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build2 --target test_cmake_build + + - name: Clean directory + run: git clean -fdx + + - name: Configure C++17 + run: cmake -G "MinGW Makefiles" -DCMAKE_CXX_STANDARD=17 -DPYBIND11_WERROR=ON -DDOWNLOAD_CATCH=ON -S . -B build3 + + - name: Build C++17 + run: cmake --build build3 -j 2 + + - name: Python tests C++17 + run: cmake --build build3 --target pytest -j 2 + + - name: C++17 tests + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target cpptest -j 2 + + - name: Interface test C++17 + run: PYTHONHOME=/${{matrix.sys}} PYTHONPATH=/${{matrix.sys}} cmake --build build3 --target test_cmake_build + + windows_clang: + + strategy: + matrix: + os: [windows-latest] + python: ['3.10'] + + runs-on: "${{ matrix.os }}" + + name: "🐍 ${{ matrix.python }} • ${{ matrix.os }} • clang-latest" + + steps: + - name: Show env + run: env + + - name: Checkout + uses: actions/checkout@v3 + + - name: Set up Clang + uses: egor-tensin/setup-clang@v1 + + - name: Setup Python ${{ matrix.python }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python }} + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.13 + + - name: Install ninja-build tool + uses: seanmiddleditch/gha-setup-ninja@v3 + + - name: Run pip installs + run: | + python -m pip install --upgrade pip + python -m pip install -r tests/requirements.txt + + - name: Show Clang++ version + run: clang++ --version + + - name: Show CMake version + run: cmake --version + + # TODO: WERROR=ON + - name: Configure Clang + run: > + cmake -G Ninja -S . -B . + -DPYBIND11_WERROR=OFF + -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_COMPILER=clang++ + -DCMAKE_CXX_STANDARD=17 + + - name: Build + run: cmake --build . -j 2 + + - name: Python tests + run: cmake --build . --target pytest -j 2 + + - name: C++ tests + run: cmake --build . --target cpptest -j 2 + + - name: Interface test + run: cmake --build . --target test_cmake_build -j 2 + + - name: Clean directory + run: git clean -fdx + + macos_brew_install_llvm: + name: "macos-latest • brew install llvm" + runs-on: macos-latest + + env: + # https://apple.stackexchange.com/questions/227026/how-to-install-recent-clang-with-homebrew + LDFLAGS: '-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib' + + steps: + - name: Update PATH + run: echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH + + - name: Show env + run: env + + - name: Checkout + uses: actions/checkout@v3 + + - name: Show Clang++ version before brew install llvm + run: clang++ --version + + - name: brew install llvm + run: brew install llvm + + - name: Show Clang++ version after brew install llvm + run: clang++ --version + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.13 + + - name: Run pip installs + run: | + python3 -m pip install --upgrade pip + python3 -m pip install -r tests/requirements.txt + python3 -m pip install numpy + python3 -m pip install scipy + + - name: Show CMake version + run: cmake --version + + - name: CMake Configure + run: > + cmake -S . -B . + -DPYBIND11_WERROR=ON + -DPYBIND11_SIMPLE_GIL_MANAGEMENT=OFF + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_COMPILER=clang++ + -DCMAKE_CXX_STANDARD=17 + -DPYTHON_EXECUTABLE=$(python3 -c "import sys; print(sys.executable)") + + - name: Build + run: cmake --build . -j 2 + + - name: Python tests + run: cmake --build . --target pytest -j 2 + + - name: C++ tests + run: cmake --build . --target cpptest -j 2 + + - name: Interface test + run: cmake --build . --target test_cmake_build -j 2 + + - name: Clean directory + run: git clean -fdx diff --git a/ext/pybind11/.github/workflows/configure.yml b/ext/pybind11/.github/workflows/configure.yml index 578dba630e..29b041168e 100644 --- a/ext/pybind11/.github/workflows/configure.yml +++ b/ext/pybind11/.github/workflows/configure.yml @@ -9,6 +9,10 @@ on: - stable - v* +env: + # For cmake: + VERBOSE: 1 + jobs: # This tests various versions of CMake in various combinations, to make sure # the configure step passes. @@ -16,12 +20,12 @@ jobs: strategy: fail-fast: false matrix: - runs-on: [ubuntu-latest, macos-latest, windows-latest] + runs-on: [ubuntu-20.04, macos-latest, windows-latest] arch: [x64] - cmake: [3.18] + cmake: ["3.23"] include: - - runs-on: ubuntu-latest + - runs-on: ubuntu-20.04 arch: x64 cmake: 3.4 @@ -29,22 +33,18 @@ jobs: arch: x64 cmake: 3.7 - - runs-on: windows-2016 - arch: x86 - cmake: 3.8 - - - runs-on: windows-2016 - arch: x86 + - runs-on: windows-2019 + arch: x64 # x86 compilers seem to be missing on 2019 image cmake: 3.18 name: 🐍 3.7 • CMake ${{ matrix.cmake }} • ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup Python 3.7 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.7 architecture: ${{ matrix.arch }} @@ -55,7 +55,7 @@ jobs: # An action for adding a specific version of CMake: # https://github.com/jwlawson/actions-setup-cmake - name: Setup CMake ${{ matrix.cmake }} - uses: jwlawson/actions-setup-cmake@v1.7 + uses: jwlawson/actions-setup-cmake@v1.13 with: cmake-version: ${{ matrix.cmake }} diff --git a/ext/pybind11/.github/workflows/format.yml b/ext/pybind11/.github/workflows/format.yml index 5cebed17da..b18474bc3d 100644 --- a/ext/pybind11/.github/workflows/format.yml +++ b/ext/pybind11/.github/workflows/format.yml @@ -12,24 +12,35 @@ on: - stable - "v*" +env: + FORCE_COLOR: 3 + # For cmake: + VERBOSE: 1 + jobs: pre-commit: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 - - uses: pre-commit/action@v2.0.0 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" + - name: Add matchers + run: echo "::add-matcher::$GITHUB_WORKSPACE/.github/matchers/pylint.json" + - uses: pre-commit/action@v3.0.0 with: # Slow hooks are marked with manual - slow is okay here, run them too extra_args: --hook-stage manual --all-files clang-tidy: + # When making changes here, please also review the "Clang-Tidy" section + # in .github/CONTRIBUTING.md and update as needed. name: Clang-Tidy runs-on: ubuntu-latest - container: silkeh/clang:10 + container: silkeh/clang:13 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install requirements run: apt-get update && apt-get install -y python3-dev python3-pytest @@ -37,10 +48,10 @@ jobs: - name: Configure run: > cmake -S . -B build - -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--warnings-as-errors=*" + -DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--use-color;--warnings-as-errors=*" -DDOWNLOAD_EIGEN=ON -DDOWNLOAD_CATCH=ON -DCMAKE_CXX_STANDARD=17 - name: Build - run: cmake --build build -j 2 + run: cmake --build build -j 2 -- --keep-going diff --git a/ext/pybind11/.github/workflows/labeler.yml b/ext/pybind11/.github/workflows/labeler.yml index d2b5979681..165a2fd87b 100644 --- a/ext/pybind11/.github/workflows/labeler.yml +++ b/ext/pybind11/.github/workflows/labeler.yml @@ -10,7 +10,11 @@ jobs: steps: - uses: actions/labeler@main - if: github.event.pull_request.merged == true + if: > + github.event.pull_request.merged == true && + !startsWith(github.event.pull_request.title, 'chore(deps):') && + !startsWith(github.event.pull_request.title, 'ci(fix):') && + !startsWith(github.event.pull_request.title, 'docs(changelog):') with: repo-token: ${{ secrets.GITHUB_TOKEN }} configuration-path: .github/labeler_merged.yml diff --git a/ext/pybind11/.github/workflows/pip.yml b/ext/pybind11/.github/workflows/pip.yml index 4414a12ee4..7c6fc67a3e 100644 --- a/ext/pybind11/.github/workflows/pip.yml +++ b/ext/pybind11/.github/workflows/pip.yml @@ -12,24 +12,28 @@ on: types: - published +env: + PIP_ONLY_BINARY: numpy + jobs: # This builds the sdists and wheels and makes sure the files are exactly as - # expected. Using Windows and Python 2.7, since that is often the most + # expected. Using Windows and Python 3.6, since that is often the most # challenging matrix element. test-packaging: - name: 🐍 2.7 • 📦 tests • windows-latest + name: 🐍 3.6 • 📦 tests • windows-latest runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Setup 🐍 2.7 - uses: actions/setup-python@v2 + - name: Setup 🐍 3.6 + uses: actions/setup-python@v4 with: - python-version: 2.7 + python-version: 3.6 - name: Prepare env - run: python -m pip install -r tests/requirements.txt --prefer-binary + run: | + python -m pip install -r tests/requirements.txt - name: Python Packaging tests run: pytest tests/extra_python_package/ @@ -42,15 +46,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Setup 🐍 3.8 - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Prepare env - run: python -m pip install -r tests/requirements.txt build twine --prefer-binary + run: | + python -m pip install -r tests/requirements.txt build twine - name: Python Packaging tests run: pytest tests/extra_python_package/ @@ -64,13 +69,13 @@ jobs: run: twine check dist/* - name: Save standard package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: standard path: dist/pybind11-* - name: Save global package - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: global path: dist/pybind11_global-* @@ -85,19 +90,21 @@ jobs: needs: [packaging] steps: - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v4 + with: + python-version: "3.x" # Downloads all to directories matching the artifact names - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 - name: Publish standard package - uses: pypa/gh-action-pypi-publish@v1.4.1 + uses: pypa/gh-action-pypi-publish@v1.6.4 with: password: ${{ secrets.pypi_password }} packages_dir: standard/ - name: Publish global package - uses: pypa/gh-action-pypi-publish@v1.4.1 + uses: pypa/gh-action-pypi-publish@v1.6.4 with: password: ${{ secrets.pypi_password_global }} packages_dir: global/ diff --git a/ext/pybind11/.github/workflows/upstream.yml b/ext/pybind11/.github/workflows/upstream.yml new file mode 100644 index 0000000000..a15861ee47 --- /dev/null +++ b/ext/pybind11/.github/workflows/upstream.yml @@ -0,0 +1,114 @@ + +name: Upstream + +on: + workflow_dispatch: + pull_request: + +concurrency: + group: upstream-${{ github.ref }} + cancel-in-progress: true + +env: + PIP_ONLY_BINARY: numpy + # For cmake: + VERBOSE: 1 + +jobs: + standard: + name: "🐍 3.11 latest internals • ubuntu-latest • x64" + runs-on: ubuntu-latest + if: "contains(github.event.pull_request.labels.*.name, 'python dev')" + + steps: + - uses: actions/checkout@v3 + + - name: Setup Python 3.11 + uses: actions/setup-python@v4 + with: + python-version: "3.11-dev" + + - name: Setup Boost (Linux) + if: runner.os == 'Linux' + run: sudo apt-get install libboost-dev + + - name: Update CMake + uses: jwlawson/actions-setup-cmake@v1.13 + + - name: Prepare env + run: | + python -m pip install -r tests/requirements.txt + + - name: Setup annotations on Linux + if: runner.os == 'Linux' + run: python -m pip install pytest-github-actions-annotate-failures + + # First build - C++11 mode and inplace + - name: Configure C++11 + run: > + cmake -S . -B . + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=11 + + - name: Build C++11 + run: cmake --build . -j 2 + + - name: Python tests C++11 + run: cmake --build . --target pytest -j 2 + + - name: C++11 tests + run: cmake --build . --target cpptest -j 2 + + - name: Interface test C++11 + run: cmake --build . --target test_cmake_build + + - name: Clean directory + run: git clean -fdx + + # Second build - C++17 mode and in a build directory + - name: Configure C++17 + run: > + cmake -S . -B build2 + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + ${{ matrix.args }} + ${{ matrix.args2 }} + + - name: Build + run: cmake --build build2 -j 2 + + - name: Python tests + run: cmake --build build2 --target pytest + + - name: C++ tests + run: cmake --build build2 --target cpptest + + # Third build - C++17 mode with unstable ABI + - name: Configure (unstable ABI) + run: > + cmake -S . -B build3 + -DPYBIND11_WERROR=ON + -DDOWNLOAD_CATCH=ON + -DDOWNLOAD_EIGEN=ON + -DCMAKE_CXX_STANDARD=17 + -DPYBIND11_INTERNALS_VERSION=10000000 + "-DPYBIND11_TEST_OVERRIDE=test_call_policies.cpp;test_gil_scoped.cpp;test_thread.cpp" + ${{ matrix.args }} + + - name: Build (unstable ABI) + run: cmake --build build3 -j 2 + + - name: Python tests (unstable ABI) + run: cmake --build build3 --target pytest + + - name: Interface test + run: cmake --build build3 --target test_cmake_build + + # This makes sure the setup_helpers module can build packages using + # setuptools + - name: Setuptools helpers test + run: pytest tests/extra_setuptools diff --git a/ext/pybind11/.gitignore b/ext/pybind11/.gitignore index 3f36b89e0c..43d5094c96 100644 --- a/ext/pybind11/.gitignore +++ b/ext/pybind11/.gitignore @@ -41,3 +41,6 @@ pybind11Targets.cmake /.vscode /pybind11/include/* /pybind11/share/* +/docs/_build/* +.ipynb_checkpoints/ +tests/main.cpp diff --git a/ext/pybind11/.pre-commit-config.yaml b/ext/pybind11/.pre-commit-config.yaml index 6781ac4f11..d625d5726b 100644 --- a/ext/pybind11/.pre-commit-config.yaml +++ b/ext/pybind11/.pre-commit-config.yaml @@ -12,49 +12,118 @@ # # See https://github.com/pre-commit/pre-commit + +ci: + autoupdate_commit_msg: "chore(deps): update pre-commit hooks" + autofix_commit_msg: "style: pre-commit fixes" + autoupdate_schedule: monthly + +# third-party content +exclude: ^tools/JoinPaths.cmake$ + repos: # Standard hooks - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: "v4.4.0" hooks: - id: check-added-large-files - id: check-case-conflict + - id: check-docstring-first - id: check-merge-conflict - id: check-symlinks + - id: check-toml - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: mixed-line-ending - id: requirements-txt-fixer - id: trailing-whitespace - - id: fix-encoding-pragma + +# Upgrade old Python syntax +- repo: https://github.com/asottile/pyupgrade + rev: "v3.3.1" + hooks: + - id: pyupgrade + args: [--py36-plus] + +# Nicely sort includes +- repo: https://github.com/PyCQA/isort + rev: "5.11.4" + hooks: + - id: isort # Black, the code formatter, natively supports pre-commit - repo: https://github.com/psf/black - rev: 20.8b1 + rev: "22.12.0" # Keep in sync with blacken-docs hooks: - id: black - # By default, this ignores pyi files, though black supports them - types: [text] - files: \.pyi?$ + +# Also code format the docs +- repo: https://github.com/asottile/blacken-docs + rev: "v1.12.1" + hooks: + - id: blacken-docs + additional_dependencies: + - black==22.10.0 # keep in sync with black hook # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.1.9 + rev: "v1.3.1" hooks: - id: remove-tabs +- repo: https://github.com/sirosen/texthooks + rev: "0.4.0" + hooks: + - id: fix-ligatures + - id: fix-smartquotes + +# Autoremoves unused imports +- repo: https://github.com/hadialqattan/pycln + rev: "v2.1.2" + hooks: + - id: pycln + stages: [manual] + +# Checking for common mistakes +- repo: https://github.com/pre-commit/pygrep-hooks + rev: "v1.9.0" + hooks: + - id: python-check-blanket-noqa + - id: python-check-blanket-type-ignore + - id: python-no-log-warn + - id: python-use-type-annotations + - id: rst-backticks + - id: rst-directive-colons + - id: rst-inline-touching-normal + +# Automatically remove noqa that are not used +- repo: https://github.com/asottile/yesqa + rev: "v1.4.0" + hooks: + - id: yesqa + additional_dependencies: &flake8_dependencies + - flake8-bugbear + - pep8-naming + # Flake8 also supports pre-commit natively (same author) -- repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 +- repo: https://github.com/PyCQA/flake8 + rev: "6.0.0" hooks: - id: flake8 - additional_dependencies: [flake8-bugbear, pep8-naming] exclude: ^(docs/.*|tools/.*)$ + additional_dependencies: *flake8_dependencies + +# PyLint has native support - not always usable, but works for us +- repo: https://github.com/PyCQA/pylint + rev: "v2.15.9" + hooks: + - id: pylint + files: ^pybind11 # CMake formatting - repo: https://github.com/cheshirekow/cmake-format-precommit - rev: v0.6.13 + rev: "v0.6.13" hooks: - id: cmake-format additional_dependencies: [pyyaml] @@ -63,38 +132,50 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.800 + rev: "v0.991" hooks: - id: mypy - # The default Python type ignores .pyi files, so let's rerun if detected - types: [text] - files: ^pybind11.*\.pyi?$ - # Running per-file misbehaves a bit, so just run on all files, it's fast - pass_filenames: false + args: [] + exclude: ^(tests|docs)/ + additional_dependencies: [nox, rich] # Checks the manifest for missing files (native support) - repo: https://github.com/mgedmin/check-manifest - rev: "0.46" + rev: "0.49" hooks: - id: check-manifest # This is a slow hook, so only run this if --hook-stage manual is passed stages: [manual] additional_dependencies: [cmake, ninja] -# The original pybind11 checks for a few C++ style items +# Check for spelling +# Use tools/codespell_ignore_lines_from_errors.py +# to rebuild .codespell-ignore-lines +- repo: https://github.com/codespell-project/codespell + rev: "v2.2.2" + hooks: + - id: codespell + exclude: ".supp$" + args: ["-x", ".codespell-ignore-lines"] + +# Check for common shell mistakes +- repo: https://github.com/shellcheck-py/shellcheck-py + rev: "v0.9.0.2" + hooks: + - id: shellcheck + +# Disallow some common capitalization mistakes - repo: local hooks: - id: disallow-caps name: Disallow improper capitalization language: pygrep - entry: PyBind|Numpy|Cmake|CCache - exclude: .pre-commit-config.yaml + entry: PyBind|Numpy|Cmake|CCache|PyTest + exclude: ^\.pre-commit-config.yaml$ -- repo: local +# Clang format the codebase automatically +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: "v15.0.6" hooks: - - id: check-style - name: Classic check-style - language: system - types: - - c++ - entry: ./tools/check-style.sh + - id: clang-format + types_or: [c++, c, cuda] diff --git a/ext/pybind11/CMakeLists.txt b/ext/pybind11/CMakeLists.txt index 2e81869c3f..0d93203881 100644 --- a/ext/pybind11/CMakeLists.txt +++ b/ext/pybind11/CMakeLists.txt @@ -16,6 +16,11 @@ else() cmake_policy(VERSION 3.22) endif() +# Avoid infinite recursion if tests include this as a subdirectory +if(DEFINED PYBIND11_MASTER_PROJECT) + return() +endif() + # Extract project version from source file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h" pybind11_version_defines REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") @@ -45,13 +50,8 @@ if(NOT pybind11_FIND_QUIETLY) message(STATUS "pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}") endif() -# Avoid infinite recursion if tests include this as a subdirectory -if(DEFINED PYBIND11_MASTER_PROJECT) - set(PYBIND11_TEST OFF) -endif() - # Check if pybind11 is being used directly or via add_subdirectory -if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR AND NOT DEFINED PYBIND11_MASTER_PROJECT) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) ### Warn if not an out-of-source builds if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) set(lines @@ -80,6 +80,8 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR AND NOT DEFINED PYBIND11_MASTER_ endif() set(pybind11_system "") + + set_property(GLOBAL PROPERTY USE_FOLDERS ON) else() set(PYBIND11_MASTER_PROJECT OFF) set(pybind11_system SYSTEM) @@ -89,10 +91,16 @@ endif() option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_NOPYTHON "Disable search for Python" OFF) +option(PYBIND11_SIMPLE_GIL_MANAGEMENT + "Use simpler GIL management logic that does not support disassociation" OFF) set(PYBIND11_INTERNALS_VERSION "" CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.") +if(PYBIND11_SIMPLE_GIL_MANAGEMENT) + add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT) +endif() + cmake_dependent_option( USE_PYTHON_INCLUDE_DIR "Install pybind11 headers in Python include directory instead of default installation prefix" @@ -118,6 +126,8 @@ set(PYBIND11_HEADERS include/pybind11/complex.h include/pybind11/options.h include/pybind11/eigen.h + include/pybind11/eigen/matrix.h + include/pybind11/eigen/tensor.h include/pybind11/embed.h include/pybind11/eval.h include/pybind11/gil.h @@ -196,6 +206,9 @@ else() endif() include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake") +# https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files +# TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet +include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake") # Relative directory setting if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS) @@ -260,6 +273,16 @@ if(PYBIND11_INSTALL) NAMESPACE "pybind11::" DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + # pkg-config support + if(NOT prefix_for_pc_file) + set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}") + endif() + join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/") + # Uninstall target if(PYBIND11_MASTER_PROJECT) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in" diff --git a/ext/pybind11/MANIFEST.in b/ext/pybind11/MANIFEST.in index aed183e874..033303a74a 100644 --- a/ext/pybind11/MANIFEST.in +++ b/ext/pybind11/MANIFEST.in @@ -1,6 +1,5 @@ recursive-include pybind11/include/pybind11 *.h recursive-include pybind11 *.py recursive-include pybind11 py.typed -recursive-include pybind11 *.pyi include pybind11/share/cmake/pybind11/*.cmake include LICENSE README.rst pyproject.toml setup.py setup.cfg diff --git a/ext/pybind11/README.rst b/ext/pybind11/README.rst index 7ce57b03ae..3c75edb575 100644 --- a/ext/pybind11/README.rst +++ b/ext/pybind11/README.rst @@ -32,9 +32,9 @@ this heavy machinery has become an excessively large and unnecessary dependency. Think of this library as a tiny self-contained version of Boost.Python -with everything stripped away that isn’t relevant for binding +with everything stripped away that isn't relevant for binding generation. Without comments, the core header files only require ~4K -lines of code and depend on Python (2.7 or 3.5+, or PyPy) and the C++ +lines of code and depend on Python (3.6+, or PyPy) and the C++ standard library. This compact implementation was possible thanks to some of the new C++11 language features (specifically: tuples, lambda functions and variadic templates). Since its creation, this library has @@ -78,8 +78,8 @@ Goodies In addition to the core functionality, pybind11 provides some extra goodies: -- Python 2.7, 3.5+, and PyPy/PyPy3 7.3 are supported with an - implementation-agnostic interface. +- Python 3.6+, and PyPy3 7.3 are supported with an implementation-agnostic + interface (pybind11 2.9 was the last version to support Python 2 and 3.5). - It is possible to bind C++11 lambda functions with captured variables. The lambda capture data is stored inside the resulting @@ -88,8 +88,8 @@ goodies: - pybind11 uses C++11 move constructors and move assignment operators whenever possible to efficiently transfer custom data types. -- It’s easy to expose the internal storage of custom data types through - Pythons’ buffer protocols. This is handy e.g. for fast conversion +- It's easy to expose the internal storage of custom data types through + Pythons' buffer protocols. This is handy e.g. for fast conversion between C++ matrix classes like Eigen and NumPy without expensive copy operations. @@ -106,7 +106,7 @@ goodies: - Binaries are generally smaller by a factor of at least 2 compared to equivalent bindings generated by Boost.Python. A recent pybind11 conversion of PyRosetta, an enormous Boost.Python binding project, - `reported `_ + `reported `_ a binary size reduction of **5.4x** and compile time reduction by **5.8x**. @@ -119,10 +119,10 @@ goodies: Supported compilers ------------------- -1. Clang/LLVM 3.3 or newer (for Apple Xcode’s clang, this is 5.0.0 or +1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or newer) 2. GCC 4.8 or newer -3. Microsoft Visual Studio 2015 Update 3 or newer +3. Microsoft Visual Studio 2017 or newer 4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI) 5. Cygwin/GCC (previously tested on 2.5.1) 6. NVCC (CUDA 11.0 tested in CI) diff --git a/ext/pybind11/docs/Doxyfile b/ext/pybind11/docs/Doxyfile index c8562952ef..09138db364 100644 --- a/ext/pybind11/docs/Doxyfile +++ b/ext/pybind11/docs/Doxyfile @@ -18,6 +18,4 @@ ALIASES += "endrst=\endverbatim" QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = NO -PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \ - PY_MAJOR_VERSION=3 \ - PYBIND11_NOINLINE +PREDEFINED = PYBIND11_NOINLINE diff --git a/ext/pybind11/docs/_static/css/custom.css b/ext/pybind11/docs/_static/css/custom.css new file mode 100644 index 0000000000..7a49a6ac4f --- /dev/null +++ b/ext/pybind11/docs/_static/css/custom.css @@ -0,0 +1,3 @@ +.highlight .go { + color: #707070; +} diff --git a/ext/pybind11/docs/_static/theme_overrides.css b/ext/pybind11/docs/_static/theme_overrides.css deleted file mode 100644 index 1071809fa0..0000000000 --- a/ext/pybind11/docs/_static/theme_overrides.css +++ /dev/null @@ -1,11 +0,0 @@ -.wy-table-responsive table td, -.wy-table-responsive table th { - white-space: initial !important; -} -.rst-content table.docutils td { - vertical-align: top !important; -} -div[class^='highlight'] pre { - white-space: pre; - white-space: pre-wrap; -} diff --git a/ext/pybind11/docs/advanced/cast/custom.rst b/ext/pybind11/docs/advanced/cast/custom.rst index 19b9353476..8138cac619 100644 --- a/ext/pybind11/docs/advanced/cast/custom.rst +++ b/ext/pybind11/docs/advanced/cast/custom.rst @@ -38,7 +38,7 @@ type is explicitly allowed. .. code-block:: cpp - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template <> struct type_caster { public: /** @@ -46,7 +46,7 @@ type is explicitly allowed. * function signatures and declares a local variable * 'value' of type inty */ - PYBIND11_TYPE_CASTER(inty, _("inty")); + PYBIND11_TYPE_CASTER(inty, const_name("inty")); /** * Conversion part 1 (Python->C++): convert a PyObject into a inty @@ -78,7 +78,7 @@ type is explicitly allowed. return PyLong_FromLong(src.long_value); } }; - }} // namespace pybind11::detail + }} // namespace PYBIND11_NAMESPACE::detail .. note:: diff --git a/ext/pybind11/docs/advanced/cast/eigen.rst b/ext/pybind11/docs/advanced/cast/eigen.rst index 80f1013430..a5c11a3f14 100644 --- a/ext/pybind11/docs/advanced/cast/eigen.rst +++ b/ext/pybind11/docs/advanced/cast/eigen.rst @@ -52,7 +52,7 @@ can be mapped *and* if the numpy array is writeable (that is the passed variable will be transparently carried out directly on the ``numpy.ndarray``. -This means you can can write code such as the following and have it work as +This means you can write code such as the following and have it work as expected: .. code-block:: cpp diff --git a/ext/pybind11/docs/advanced/cast/overview.rst b/ext/pybind11/docs/advanced/cast/overview.rst index 6341fce6d4..011bd4c7a3 100644 --- a/ext/pybind11/docs/advanced/cast/overview.rst +++ b/ext/pybind11/docs/advanced/cast/overview.rst @@ -75,97 +75,96 @@ The following basic data types are supported out of the box (some may require an additional extension header to be included). To pass other data structures as arguments and return values, refer to the section on binding :ref:`classes`. -+------------------------------------+---------------------------+-------------------------------+ -| Data type | Description | Header file | -+====================================+===========================+===============================+ -| ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``char`` | Character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``char16_t`` | UTF-16 character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``char32_t`` | UTF-32 character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const char16_t *`` | UTF-16 string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const char32_t *`` | UTF-32 string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::u16string`` | STL dynamic UTF-16 string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::u32string`` | STL dynamic UTF-32 string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` | -| ``std::u16string_view``, etc. | | | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::pair`` | Pair of two custom types | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::complex`` | Complex numbers | :file:`pybind11/complex.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::array`` | STL static array | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::vector`` | STL dynamic array | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::deque`` | STL double-ended queue | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::valarray`` | STL value array | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::list`` | STL linked list | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::map`` | STL ordered map | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::unordered_map`` | STL unordered map | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::set`` | STL ordered set | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::unordered_set`` | STL unordered set | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::optional`` | STL optional type (C++17) | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::experimental::optional`` | STL optional type (exp.) | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::filesystem::path`` | STL path (C++17) [#]_ | :file:`pybind11/stl.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``std::chrono::time_point<...>`` | STL date/time | :file:`pybind11/chrono.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``Eigen::Matrix<...>`` | Eigen: dense matrix | :file:`pybind11/eigen.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``Eigen::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` | -+------------------------------------+---------------------------+-------------------------------+ -| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` | -+------------------------------------+---------------------------+-------------------------------+ ++------------------------------------+---------------------------+-----------------------------------+ +| Data type | Description | Header file | ++====================================+===========================+===================================+ +| ``int8_t``, ``uint8_t`` | 8-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``int16_t``, ``uint16_t`` | 16-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``int32_t``, ``uint32_t`` | 32-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``int64_t``, ``uint64_t`` | 64-bit integers | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``ssize_t``, ``size_t`` | Platform-dependent size | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``float``, ``double`` | Floating point types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``bool`` | Two-state Boolean type | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``char`` | Character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``char16_t`` | UTF-16 character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``char32_t`` | UTF-32 character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``wchar_t`` | Wide character literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const char *`` | UTF-8 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const char16_t *`` | UTF-16 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const char32_t *`` | UTF-32 string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``const wchar_t *`` | Wide string literal | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::string`` | STL dynamic UTF-8 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::u16string`` | STL dynamic UTF-16 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::u32string`` | STL dynamic UTF-32 string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::wstring`` | STL dynamic wide string | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::string_view``, | STL C++17 string views | :file:`pybind11/pybind11.h` | +| ``std::u16string_view``, etc. | | | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::pair`` | Pair of two custom types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::tuple<...>`` | Arbitrary tuple of types | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::reference_wrapper<...>`` | Reference type wrapper | :file:`pybind11/pybind11.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::complex`` | Complex numbers | :file:`pybind11/complex.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::array`` | STL static array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::vector`` | STL dynamic array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::deque`` | STL double-ended queue | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::valarray`` | STL value array | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::list`` | STL linked list | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::map`` | STL ordered map | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::unordered_map`` | STL unordered map | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::set`` | STL ordered set | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::unordered_set`` | STL unordered set | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::optional`` | STL optional type (C++17) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::experimental::optional`` | STL optional type (exp.) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::variant<...>`` | Type-safe union (C++17) | :file:`pybind11/stl.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::filesystem::path`` | STL path (C++17) [#]_ | :file:`pybind11/stl/filesystem.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::function<...>`` | STL polymorphic function | :file:`pybind11/functional.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::chrono::duration<...>`` | STL time duration | :file:`pybind11/chrono.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``std::chrono::time_point<...>`` | STL date/time | :file:`pybind11/chrono.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``Eigen::Matrix<...>`` | Eigen: dense matrix | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``Eigen::Map<...>`` | Eigen: mapped memory | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-----------------------------------+ +| ``Eigen::SparseMatrix<...>`` | Eigen: sparse matrix | :file:`pybind11/eigen.h` | ++------------------------------------+---------------------------+-----------------------------------+ .. [#] ``std::filesystem::path`` is converted to ``pathlib.Path`` and - ``os.PathLike`` is converted to ``std::filesystem::path``, but this requires - Python 3.6 (for ``__fspath__`` support). + ``os.PathLike`` is converted to ``std::filesystem::path``. diff --git a/ext/pybind11/docs/advanced/cast/stl.rst b/ext/pybind11/docs/advanced/cast/stl.rst index b8622ee095..03d49b2950 100644 --- a/ext/pybind11/docs/advanced/cast/stl.rst +++ b/ext/pybind11/docs/advanced/cast/stl.rst @@ -42,7 +42,7 @@ types: .. code-block:: cpp // `boost::optional` as an example -- can be any `std::optional`-like container - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template struct type_caster> : optional_caster> {}; }} @@ -54,7 +54,7 @@ for custom variant types: .. code-block:: cpp // `boost::variant` as an example -- can be any `std::variant`-like container - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template struct type_caster> : variant_caster> {}; @@ -66,7 +66,7 @@ for custom variant types: return boost::apply_visitor(args...); } }; - }} // namespace pybind11::detail + }} // namespace PYBIND11_NAMESPACE::detail The ``visit_helper`` specialization is not required if your ``name::variant`` provides a ``name::visit()`` function. For any other function name, the specialization must be @@ -87,8 +87,6 @@ included to tell pybind11 how to visit the variant. pybind11 only supports the modern implementation of ``boost::variant`` which makes use of variadic templates. This requires Boost 1.56 or newer. - Additionally, on Windows, MSVC 2017 is required because ``boost::variant`` - falls back to the old non-variadic implementation on MSVC 2015. .. _opaque: diff --git a/ext/pybind11/docs/advanced/cast/strings.rst b/ext/pybind11/docs/advanced/cast/strings.rst index cfd7e7b7a5..e246c5219a 100644 --- a/ext/pybind11/docs/advanced/cast/strings.rst +++ b/ext/pybind11/docs/advanced/cast/strings.rst @@ -1,14 +1,6 @@ Strings, bytes and Unicode conversions ###################################### -.. note:: - - This section discusses string handling in terms of Python 3 strings. For - Python 2.7, replace all occurrences of ``str`` with ``unicode`` and - ``bytes`` with ``str``. Python 2.7 users may find it best to use ``from - __future__ import unicode_literals`` to avoid unintentionally using ``str`` - instead of ``unicode``. - Passing Python strings to C++ ============================= @@ -58,9 +50,9 @@ Passing bytes to C++ -------------------- A Python ``bytes`` object will be passed to C++ functions that accept -``std::string`` or ``char*`` *without* conversion. On Python 3, in order to -make a function *only* accept ``bytes`` (and not ``str``), declare it as taking -a ``py::bytes`` argument. +``std::string`` or ``char*`` *without* conversion. In order to make a function +*only* accept ``bytes`` (and not ``str``), declare it as taking a ``py::bytes`` +argument. Returning C++ strings to Python @@ -204,11 +196,6 @@ decoded to Python ``str``. } ); -.. warning:: - - Wide character strings may not work as described on Python 2.7 or Python - 3.3 compiled with ``--enable-unicode=ucs2``. - Strings in multibyte encodings such as Shift-JIS must transcoded to a UTF-8/16/32 before being returned to Python. diff --git a/ext/pybind11/docs/advanced/classes.rst b/ext/pybind11/docs/advanced/classes.rst index 6330af5eb4..01a490b721 100644 --- a/ext/pybind11/docs/advanced/classes.rst +++ b/ext/pybind11/docs/advanced/classes.rst @@ -9,7 +9,7 @@ that you are already familiar with the basics from :doc:`/classes`. Overriding virtual functions in Python ====================================== -Suppose that a C++ class or interface has a virtual function that we'd like to +Suppose that a C++ class or interface has a virtual function that we'd like to override from within Python (we'll focus on the class ``Animal``; ``Dog`` is given as a specific example of how one would do this with traditional C++ code). @@ -133,14 +133,14 @@ a virtual method call. >>> from example import * >>> d = Dog() >>> call_go(d) - u'woof! woof! woof! ' + 'woof! woof! woof! ' >>> class Cat(Animal): ... def go(self, n_times): ... return "meow! " * n_times ... >>> c = Cat() >>> call_go(c) - u'meow! meow! meow! ' + 'meow! meow! meow! ' If you are defining a custom constructor in a derived Python class, you *must* ensure that you explicitly call the bound C++ constructor using ``__init__``, @@ -813,26 +813,21 @@ An instance can now be pickled as follows: .. code-block:: python - try: - import cPickle as pickle # Use cPickle on Python 2.7 - except ImportError: - import pickle + import pickle p = Pickleable("test_value") p.setExtra(15) - data = pickle.dumps(p, 2) + data = pickle.dumps(p) .. note:: - Note that only the cPickle module is supported on Python 2.7. - - The second argument to ``dumps`` is also crucial: it selects the pickle - protocol version 2, since the older version 1 is not supported. Newer - versions are also fine—for instance, specify ``-1`` to always use the - latest available version. Beware: failure to follow these instructions - will cause important pybind11 memory allocation routines to be skipped - during unpickling, which will likely lead to memory corruption and/or - segmentation faults. + If given, the second argument to ``dumps`` must be 2 or larger - 0 and 1 are + not supported. Newer versions are also fine; for instance, specify ``-1`` to + always use the latest available version. Beware: failure to follow these + instructions will cause important pybind11 memory allocation routines to be + skipped during unpickling, which will likely lead to memory corruption + and/or segmentation faults. Python defaults to version 3 (Python 3-3.7) and + version 4 for Python 3.8+. .. seealso:: @@ -849,11 +844,9 @@ Python normally uses references in assignments. Sometimes a real copy is needed to prevent changing all copies. The ``copy`` module [#f5]_ provides these capabilities. -On Python 3, a class with pickle support is automatically also (deep)copy +A class with pickle support is automatically also (deep)copy compatible. However, performance can be improved by adding custom -``__copy__`` and ``__deepcopy__`` methods. With Python 2.7, these custom methods -are mandatory for (deep)copy compatibility, because pybind11 only supports -cPickle. +``__copy__`` and ``__deepcopy__`` methods. For simple classes (deep)copy can be enabled by using the copy constructor, which should look as follows: @@ -1125,13 +1118,6 @@ described trampoline: py::class_(m, "A") // <-- `Trampoline` here .def("foo", &Publicist::foo); // <-- `Publicist` here, not `Trampoline`! -.. note:: - - MSVC 2015 has a compiler bug (fixed in version 2017) which - requires a more explicit function binding in the form of - ``.def("foo", static_cast(&Publicist::foo));`` - where ``int (A::*)() const`` is the type of ``A::foo``. - Binding final classes ===================== @@ -1161,6 +1147,58 @@ error: .. versionadded:: 2.6 +Binding classes with template parameters +======================================== + +pybind11 can also wrap classes that have template parameters. Consider these classes: + +.. code-block:: cpp + + struct Cat {}; + struct Dog {}; + + template + struct Cage { + Cage(PetType& pet); + PetType& get(); + }; + +C++ templates may only be instantiated at compile time, so pybind11 can only +wrap instantiated templated classes. You cannot wrap a non-instantiated template: + +.. code-block:: cpp + + // BROKEN (this will not compile) + py::class_(m, "Cage"); + .def("get", &Cage::get); + +You must explicitly specify each template/type combination that you want to +wrap separately. + +.. code-block:: cpp + + // ok + py::class_>(m, "CatCage") + .def("get", &Cage::get); + + // ok + py::class_>(m, "DogCage") + .def("get", &Cage::get); + +If your class methods have template parameters you can wrap those as well, +but once again each instantiation must be explicitly specified: + +.. code-block:: cpp + + typename + struct MyClass { + template + T fn(V v); + }; + + py::class>(m, "MyClassT") + .def("fn", &MyClass::fn); + Custom automatic downcasters ============================ @@ -1190,7 +1228,7 @@ whether a downcast is safe, you can proceed by specializing the std::string bark() const { return sound; } }; - namespace pybind11 { + namespace PYBIND11_NAMESPACE { template<> struct polymorphic_type_hook { static const void *get(const Pet *src, const std::type_info*& type) { // note that src may be nullptr @@ -1201,7 +1239,7 @@ whether a downcast is safe, you can proceed by specializing the return src; } }; - } // namespace pybind11 + } // namespace PYBIND11_NAMESPACE When pybind11 wants to convert a C++ pointer of type ``Base*`` to a Python object, it calls ``polymorphic_type_hook::get()`` to diff --git a/ext/pybind11/docs/advanced/exceptions.rst b/ext/pybind11/docs/advanced/exceptions.rst index 40f67d7b83..53981dc08f 100644 --- a/ext/pybind11/docs/advanced/exceptions.rst +++ b/ext/pybind11/docs/advanced/exceptions.rst @@ -64,7 +64,7 @@ at its exception handler. +--------------------------------------+--------------------------------------+ Exception translation is not bidirectional. That is, *catching* the C++ -exceptions defined above above will not trap exceptions that originate from +exceptions defined above will not trap exceptions that originate from Python. For that, catch :class:`pybind11::error_already_set`. See :ref:`below ` for further details. @@ -177,9 +177,12 @@ section. may be explicitly (re-)thrown to delegate it to the other, previously-declared existing exception translators. - Note that ``libc++`` and ``libstdc++`` `behave differently `_ - with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI boundaries need to be explicitly exported, as exercised in ``tests/test_exceptions.h``. - See also: "Problems with C++ exceptions" under `GCC Wiki `_. + Note that ``libc++`` and ``libstdc++`` `behave differently under macOS + `_ + with ``-fvisibility=hidden``. Therefore exceptions that are used across ABI + boundaries need to be explicitly exported, as exercised in + ``tests/test_exceptions.h``. See also: + "Problems with C++ exceptions" under `GCC Wiki `_. Local vs Global Exception Translators @@ -328,8 +331,8 @@ an invalid state. Chaining exceptions ('raise from') ================================== -In Python 3.3 a mechanism for indicating that exceptions were caused by other -exceptions was introduced: +Python has a mechanism for indicating that exceptions were caused by other +exceptions: .. code-block:: py @@ -340,7 +343,7 @@ exceptions was introduced: To do a similar thing in pybind11, you can use the ``py::raise_from`` function. It sets the current python error indicator, so to continue propagating the exception -you should ``throw py::error_already_set()`` (Python 3 only). +you should ``throw py::error_already_set()``. .. code-block:: cpp diff --git a/ext/pybind11/docs/advanced/functions.rst b/ext/pybind11/docs/advanced/functions.rst index abd1084ab5..69e3d8a1df 100644 --- a/ext/pybind11/docs/advanced/functions.rst +++ b/ext/pybind11/docs/advanced/functions.rst @@ -120,7 +120,7 @@ targeted arguments can be passed through the :class:`cpp_function` constructor: .. code-block:: cpp class_(m, "MyClass") - .def_property("data" + .def_property("data", py::cpp_function(&MyClass::getData, py::return_value_policy::copy), py::cpp_function(&MyClass::setData) ); @@ -306,8 +306,9 @@ The class ``py::args`` derives from ``py::tuple`` and ``py::kwargs`` derives from ``py::dict``. You may also use just one or the other, and may combine these with other -arguments as long as the ``py::args`` and ``py::kwargs`` arguments are the last -arguments accepted by the function. +arguments. Note, however, that ``py::kwargs`` must always be the last argument +of the function, and ``py::args`` implies that any further arguments are +keyword-only (see :ref:`keyword_only_arguments`). Please refer to the other examples for details on how to iterate over these, and on how to cast their entries into C++ objects. A demonstration is also @@ -366,10 +367,12 @@ like so: py::class_("MyClass") .def("myFunction", py::arg("arg") = static_cast(nullptr)); +.. _keyword_only_arguments: + Keyword-only arguments ====================== -Python 3 introduced keyword-only arguments by specifying an unnamed ``*`` +Python implements keyword-only arguments by specifying an unnamed ``*`` argument in a function definition: .. code-block:: python @@ -392,11 +395,19 @@ argument annotations when registering the function: m.def("f", [](int a, int b) { /* ... */ }, py::arg("a"), py::kw_only(), py::arg("b")); -Note that you currently cannot combine this with a ``py::args`` argument. This -feature does *not* require Python 3 to work. - .. versionadded:: 2.6 +A ``py::args`` argument implies that any following arguments are keyword-only, +as if ``py::kw_only()`` had been specified in the same relative location of the +argument list as the ``py::args`` argument. The ``py::kw_only()`` may be +included to be explicit about this, but is not required. + +.. versionchanged:: 2.9 + This can now be combined with ``py::args``. Before, ``py::args`` could only + occur at the end of the argument list, or immediately before a ``py::kwargs`` + argument at the end. + + Positional-only arguments ========================= @@ -566,3 +577,38 @@ prefers earlier-defined overloads to later-defined ones. .. versionadded:: 2.6 The ``py::prepend()`` tag. + +Binding functions with template parameters +========================================== + +You can bind functions that have template parameters. Here's a function: + +.. code-block:: cpp + + template + void set(T t); + +C++ templates cannot be instantiated at runtime, so you cannot bind the +non-instantiated function: + +.. code-block:: cpp + + // BROKEN (this will not compile) + m.def("set", &set); + +You must bind each instantiated function template separately. You may bind +each instantiation with the same name, which will be treated the same as +an overloaded function: + +.. code-block:: cpp + + m.def("set", &set); + m.def("set", &set); + +Sometimes it's more clear to bind them with separate names, which is also +an option: + +.. code-block:: cpp + + m.def("setInt", &set); + m.def("setString", &set); diff --git a/ext/pybind11/docs/advanced/misc.rst b/ext/pybind11/docs/advanced/misc.rst index edab15fcb7..805ec838fc 100644 --- a/ext/pybind11/docs/advanced/misc.rst +++ b/ext/pybind11/docs/advanced/misc.rst @@ -39,15 +39,42 @@ The ``PYBIND11_MAKE_OPAQUE`` macro does *not* require the above workarounds. Global Interpreter Lock (GIL) ============================= -When calling a C++ function from Python, the GIL is always held. +The Python C API dictates that the Global Interpreter Lock (GIL) must always +be held by the current thread to safely access Python objects. As a result, +when Python calls into C++ via pybind11 the GIL must be held, and pybind11 +will never implicitly release the GIL. + +.. code-block:: cpp + + void my_function() { + /* GIL is held when this function is called from Python */ + } + + PYBIND11_MODULE(example, m) { + m.def("my_function", &my_function); + } + +pybind11 will ensure that the GIL is held when it knows that it is calling +Python code. For example, if a Python callback is passed to C++ code via +``std::function``, when C++ code calls the function the built-in wrapper +will acquire the GIL before calling the Python callback. Similarly, the +``PYBIND11_OVERRIDE`` family of macros will acquire the GIL before calling +back into Python. + +When writing C++ code that is called from other C++ code, if that code accesses +Python state, it must explicitly acquire and release the GIL. + The classes :class:`gil_scoped_release` and :class:`gil_scoped_acquire` can be used to acquire and release the global interpreter lock in the body of a C++ function call. In this way, long-running C++ code can be parallelized using -multiple Python threads. Taking :ref:`overriding_virtuals` as an example, this +multiple Python threads, **but great care must be taken** when any +:class:`gil_scoped_release` appear: if there is any way that the C++ code +can access Python objects, :class:`gil_scoped_acquire` should be used to +reacquire the GIL. Taking :ref:`overriding_virtuals` as an example, this could be realized as follows (important changes highlighted): .. code-block:: cpp - :emphasize-lines: 8,9,31,32 + :emphasize-lines: 8,30,31 class PyAnimal : public Animal { public: @@ -56,9 +83,7 @@ could be realized as follows (important changes highlighted): /* Trampoline (need one for each virtual function) */ std::string go(int n_times) { - /* Acquire GIL before calling Python code */ - py::gil_scoped_acquire acquire; - + /* PYBIND11_OVERRIDE_PURE will acquire the GIL before accessing Python state */ PYBIND11_OVERRIDE_PURE( std::string, /* Return type */ Animal, /* Parent class */ @@ -78,7 +103,8 @@ could be realized as follows (important changes highlighted): .def(py::init<>()); m.def("call_go", [](Animal *animal) -> std::string { - /* Release GIL before calling into (potentially long-running) C++ code */ + // GIL is held when called from Python code. Release GIL before + // calling into (potentially long-running) C++ code py::gil_scoped_release release; return call_go(animal); }); @@ -92,6 +118,34 @@ The ``call_go`` wrapper can also be simplified using the ``call_guard`` policy m.def("call_go", &call_go, py::call_guard()); +Common Sources Of Global Interpreter Lock Errors +================================================================== + +Failing to properly hold the Global Interpreter Lock (GIL) is one of the +more common sources of bugs within code that uses pybind11. If you are +running into GIL related errors, we highly recommend you consult the +following checklist. + +- Do you have any global variables that are pybind11 objects or invoke + pybind11 functions in either their constructor or destructor? You are generally + not allowed to invoke any Python function in a global static context. We recommend + using lazy initialization and then intentionally leaking at the end of the program. + +- Do you have any pybind11 objects that are members of other C++ structures? One + commonly overlooked requirement is that pybind11 objects have to increase their reference count + whenever their copy constructor is called. Thus, you need to be holding the GIL to invoke + the copy constructor of any C++ class that has a pybind11 member. This can sometimes be very + tricky to track for complicated programs Think carefully when you make a pybind11 object + a member in another struct. + +- C++ destructors that invoke Python functions can be particularly troublesome as + destructors can sometimes get invoked in weird and unexpected circumstances as a result + of exceptions. + +- You should try running your code in a debug build. That will enable additional assertions + within pybind11 that will throw exceptions on certain GIL handling errors + (reference counting operations). + Binding sequence data types, iterators, the slicing protocol, etc. ================================================================== @@ -298,6 +352,15 @@ The class ``options`` allows you to selectively suppress auto-generated signatur m.def("add", [](int a, int b) { return a + b; }, "A function which adds two numbers"); } +pybind11 also appends all members of an enum to the resulting enum docstring. +This default behavior can be disabled by using the ``disable_enum_members_docstring()`` +function of the ``options`` class. + +With ``disable_user_defined_docstrings()`` all user defined docstrings of +``module_::def()``, ``class_::def()`` and ``enum_()`` are disabled, but the +function signatures and enum members are included in the docstring, unless they +are disabled separately. + Note that changes to the settings affect only function bindings created during the lifetime of the ``options`` instance. When it goes out of scope at the end of the module's init function, the default settings are restored to prevent unwanted side effects. diff --git a/ext/pybind11/docs/advanced/pycpp/numpy.rst b/ext/pybind11/docs/advanced/pycpp/numpy.rst index 30daeefff9..07c969305d 100644 --- a/ext/pybind11/docs/advanced/pycpp/numpy.rst +++ b/ext/pybind11/docs/advanced/pycpp/numpy.rst @@ -87,7 +87,7 @@ buffer objects (e.g. a NumPy matrix). /* Request a buffer descriptor from Python */ py::buffer_info info = b.request(); - /* Some sanity checks ... */ + /* Some basic validation checks ... */ if (info.format != py::format_descriptor::format()) throw std::runtime_error("Incompatible format: expected a double array!"); @@ -395,11 +395,9 @@ uses of ``py::array``: Ellipsis ======== -Python 3 provides a convenient ``...`` ellipsis notation that is often used to +Python provides a convenient ``...`` ellipsis notation that is often used to slice multidimensional arrays. For instance, the following snippet extracts the middle dimensions of a tensor with the first and last index set to zero. -In Python 2, the syntactic sugar ``...`` is not available, but the singleton -``Ellipsis`` (of type ``ellipsis``) can still be used directly. .. code-block:: python @@ -414,8 +412,6 @@ operation on the C++ side: py::array a = /* A NumPy array */; py::array b = a[py::make_tuple(0, py::ellipsis(), 0)]; -.. versionchanged:: 2.6 - ``py::ellipsis()`` is now also available in Python 2. Memory view =========== @@ -437,7 +433,7 @@ following: { 2, 4 }, // shape (rows, cols) { sizeof(uint8_t) * 4, sizeof(uint8_t) } // strides in bytes ); - }) + }); This approach is meant for providing a ``memoryview`` for a C/C++ buffer not managed by Python. The user is responsible for managing the lifetime of the @@ -453,11 +449,7 @@ We can also use ``memoryview::from_memory`` for a simple 1D contiguous buffer: buffer, // buffer pointer sizeof(uint8_t) * 8 // buffer size ); - }) - -.. note:: - - ``memoryview::from_memory`` is not available in Python 2. + }); .. versionchanged:: 2.6 ``memoryview::from_memory`` added. diff --git a/ext/pybind11/docs/advanced/smart_ptrs.rst b/ext/pybind11/docs/advanced/smart_ptrs.rst index 5a22201095..3c40ce1237 100644 --- a/ext/pybind11/docs/advanced/smart_ptrs.rst +++ b/ext/pybind11/docs/advanced/smart_ptrs.rst @@ -157,7 +157,7 @@ specialized: PYBIND11_DECLARE_HOLDER_TYPE(T, SmartPtr); // Only needed if the type's `.get()` goes by another name - namespace pybind11 { namespace detail { + namespace PYBIND11_NAMESPACE { namespace detail { template struct holder_helper> { // <-- specialization static const T *get(const SmartPtr &p) { return p.getPointer(); } diff --git a/ext/pybind11/docs/basics.rst b/ext/pybind11/docs/basics.rst index 0b1d85cfd3..e9b24c7fa7 100644 --- a/ext/pybind11/docs/basics.rst +++ b/ext/pybind11/docs/basics.rst @@ -32,8 +32,7 @@ The last line will both compile and run the tests. Windows ------- -On Windows, only **Visual Studio 2015** and newer are supported since pybind11 relies -on various C++11 language features that break older versions of Visual Studio. +On Windows, only **Visual Studio 2017** and newer are supported. .. Note:: @@ -109,7 +108,7 @@ a file named :file:`example.cpp` with the following contents: PYBIND11_MODULE(example, m) { m.doc() = "pybind11 example plugin"; // optional module docstring - m.def("add", &add, "A function which adds two numbers"); + m.def("add", &add, "A function that adds two numbers"); } .. [#f1] In practice, implementation and binding code will generally be located @@ -166,12 +165,12 @@ load and execute the example: .. code-block:: pycon $ python - Python 2.7.10 (default, Aug 22 2015, 20:33:39) - [GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.0.59.1)] on darwin + Python 3.9.10 (main, Jan 15 2022, 11:48:04) + [Clang 13.0.0 (clang-1300.0.29.3)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import example >>> example.add(1, 2) - 3L + 3 >>> .. _keyword_args: diff --git a/ext/pybind11/docs/benchmark.py b/ext/pybind11/docs/benchmark.py index f190793671..2150b6ca78 100644 --- a/ext/pybind11/docs/benchmark.py +++ b/ext/pybind11/docs/benchmark.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import datetime as dt import os import random @@ -12,20 +11,20 @@ def generate_dummy_code_pybind11(nclasses=10): bindings = "" for cl in range(nclasses): - decl += "class cl%03i;\n" % cl + decl += f"class cl{cl:03};\n" decl += "\n" for cl in range(nclasses): - decl += "class cl%03i {\n" % cl + decl += f"class {cl:03} {{\n" decl += "public:\n" - bindings += ' py::class_(m, "cl%03i")\n' % (cl, cl) + bindings += f' py::class_(m, "cl{cl:03}")\n' for fn in range(nfns): ret = random.randint(0, nclasses - 1) params = [random.randint(0, nclasses - 1) for i in range(nargs)] - decl += " cl%03i *fn_%03i(" % (ret, fn) - decl += ", ".join("cl%03i *" % p for p in params) + decl += f" cl{ret:03} *fn_{fn:03}(" + decl += ", ".join(f"cl{p:03} *" for p in params) decl += ");\n" - bindings += ' .def("fn_%03i", &cl%03i::fn_%03i)\n' % (fn, cl, fn) + bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03})\n' decl += "};\n\n" bindings += " ;\n" @@ -43,23 +42,20 @@ def generate_dummy_code_boost(nclasses=10): bindings = "" for cl in range(nclasses): - decl += "class cl%03i;\n" % cl + decl += f"class cl{cl:03};\n" decl += "\n" for cl in range(nclasses): decl += "class cl%03i {\n" % cl decl += "public:\n" - bindings += ' py::class_("cl%03i")\n' % (cl, cl) + bindings += f' py::class_("cl{cl:03}")\n' for fn in range(nfns): ret = random.randint(0, nclasses - 1) params = [random.randint(0, nclasses - 1) for i in range(nargs)] - decl += " cl%03i *fn_%03i(" % (ret, fn) - decl += ", ".join("cl%03i *" % p for p in params) + decl += f" cl{ret:03} *fn_{fn:03}(" + decl += ", ".join(f"cl{p:03} *" for p in params) decl += ");\n" - bindings += ( - ' .def("fn_%03i", &cl%03i::fn_%03i, py::return_value_policy())\n' - % (fn, cl, fn) - ) + bindings += f' .def("fn_{fn:03}", &cl{cl:03}::fn_{fn:03}, py::return_value_policy())\n' decl += "};\n\n" bindings += " ;\n" @@ -75,7 +71,7 @@ def generate_dummy_code_boost(nclasses=10): for codegen in [generate_dummy_code_pybind11, generate_dummy_code_boost]: print("{") for i in range(0, 10): - nclasses = 2 ** i + nclasses = 2**i with open("test.cpp", "w") as f: f.write(codegen(nclasses)) n1 = dt.datetime.now() diff --git a/ext/pybind11/docs/changelog.rst b/ext/pybind11/docs/changelog.rst index bb5457eec1..bb111c5f26 100644 --- a/ext/pybind11/docs/changelog.rst +++ b/ext/pybind11/docs/changelog.rst @@ -6,10 +6,618 @@ Changelog Starting with version 1.8.0, pybind11 releases use a `semantic versioning `_ policy. +Changes will be added here periodically from the "Suggested changelog entry" +block in pull request descriptions. + IN DEVELOPMENT -------------- +Changes will be summarized here periodically. + +Changes: + +* ``PyGILState_Check()``'s in ``pybind11::handle``'s ``inc_ref()`` & + ``dec_ref()`` are now enabled by default again. + `#4246 `_ + +Build system improvements: + +* Update clang-tidy to 15 in CI. + `#4387 `_ + + +Version 2.10.3 (Jan 3, 2023) +---------------------------- + +Changes: + +* Temporarily made our GIL status assertions (added in 2.10.2) disabled by + default (re-enable manually by defining + ``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF``, will be enabled in 2.11). + `#4432 `_ + +* Improved error messages when ``inc_ref``/``dec_ref`` are called with an + invalid GIL state. + `#4427 `_ + `#4436 `_ + +Bug Fixes: + +* Some minor touchups found by static analyzers. + `#4440 `_ + + +Version 2.10.2 (Dec 20, 2022) +----------------------------- + +Changes: + +* ``scoped_interpreter`` constructor taking ``PyConfig``. + `#4330 `_ + +* ``pybind11/eigen/tensor.h`` adds converters to and from ``Eigen::Tensor`` and + ``Eigen::TensorMap``. + `#4201 `_ + +* ``PyGILState_Check()``'s were integrated to ``pybind11::handle`` + ``inc_ref()`` & ``dec_ref()``. The added GIL checks are guarded by + ``PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF``, which is the default only if + ``NDEBUG`` is not defined. (Made non-default in 2.10.3, will be active in 2.11) + `#4246 `_ + +* Add option for enable/disable enum members in docstring. + `#2768 `_ + +* Fixed typing of ``KeysView``, ``ValuesView`` and ``ItemsView`` in ``bind_map``. + `#4353 `_ + +Bug fixes: + +* Bug fix affecting only Python 3.6 under very specific, uncommon conditions: + move ``PyEval_InitThreads()`` call to the correct location. + `#4350 `_ + +* Fix segfault bug when passing foreign native functions to functional.h. + `#4254 `_ + +Build system improvements: + +* Support setting PYTHON_LIBRARIES manually for Windows ARM cross-compilation + (classic mode). + `#4406 `_ + +* Extend IPO/LTO detection for ICX (a.k.a IntelLLVM) compiler. + `#4402 `_ + +* Allow calling ``find_package(pybind11 CONFIG)`` multiple times from separate + directories in the same CMake project and properly link Python (new mode). + `#4401 `_ + +* ``multiprocessing_set_spawn`` in pytest fixture for added safety. + `#4377 `_ + +* Fixed a bug in two pybind11/tools cmake scripts causing "Unknown arguments specified" errors. + `#4327 `_ + + + +Version 2.10.1 (Oct 31, 2022) +----------------------------- + +This is the first version to fully support embedding the newly released Python 3.11. + +Changes: + +* Allow ``pybind11::capsule`` constructor to take null destructor pointers. + `#4221 `_ + +* ``embed.h`` was changed so that ``PYTHONPATH`` is used also with Python 3.11 + (established behavior). + `#4119 `_ + +* A ``PYBIND11_SIMPLE_GIL_MANAGEMENT`` option was added (cmake, C++ define), + along with many additional tests in ``test_gil_scoped.py``. The option may be + useful to try when debugging GIL-related issues, to determine if the more + complex default implementation is or is not to blame. See #4216 for + background. WARNING: Please be careful to not create ODR violations when + using the option: everything that is linked together with mutual symbol + visibility needs to be rebuilt. + `#4216 `_ + +* ``PYBIND11_EXPORT_EXCEPTION`` was made non-empty only under macOS. This makes + Linux builds safer, and enables the removal of warning suppression pragmas for + Windows. + `#4298 `_ + +Bug fixes: + +* Fixed a bug where ``UnicodeDecodeError`` was not propagated from various + ``py::str`` ctors when decoding surrogate utf characters. + `#4294 `_ + +* Revert perfect forwarding for ``make_iterator``. This broke at least one + valid use case. May revisit later. + `#4234 `_ + +* Fix support for safe casts to ``void*`` (regression in 2.10.0). + `#4275 `_ + +* Fix ``char8_t`` support (regression in 2.9). + `#4278 `_ + +* Unicode surrogate character in Python exception message leads to process + termination in ``error_already_set::what()``. + `#4297 `_ + +* Fix MSVC 2019 v.1924 & C++14 mode error for ``overload_cast``. + `#4188 `_ + +* Make augmented assignment operators non-const for the object-api. Behavior + was previously broken for augmented assignment operators. + `#4065 `_ + +* Add proper error checking to C++ bindings for Python list append and insert. + `#4208 `_ + +* Work-around for Nvidia's CUDA nvcc compiler in versions 11.4.0 - 11.8.0. + `#4220 `_ + +* A workaround for PyPy was added in the ``py::error_already_set`` + implementation, related to PR `#1895 `_ + released with v2.10.0. + `#4079 `_ + +* Fixed compiler errors when C++23 ``std::forward_like`` is available. + `#4136 `_ + +* Properly raise exceptions in contains methods (like when an object in unhashable). + `#4209 `_ + +* Further improve another error in exception handling. + `#4232 `_ + +* ``get_local_internals()`` was made compatible with + ``finalize_interpreter()``, fixing potential freezes during interpreter + finalization. + `#4192 `_ + +Performance and style: + +* Reserve space in set and STL map casters if possible. This will prevent + unnecessary rehashing / resizing by knowing the number of keys ahead of time + for Python to C++ casting. This improvement will greatly speed up the casting + of large unordered maps and sets. + `#4194 `_ + +* GIL RAII scopes are non-copyable to avoid potential bugs. + `#4183 `_ + +* Explicitly default all relevant ctors for pytypes in the ``PYBIND11_OBJECT`` + macros and enforce the clang-tidy checks ``modernize-use-equals-default`` in + macros as well. + `#4017 `_ + +* Optimize iterator advancement in C++ bindings. + `#4237 `_ + +* Use the modern ``PyObject_GenericGetDict`` and ``PyObject_GenericSetDict`` + for handling dynamic attribute dictionaries. + `#4106 `_ + +* Document that users should use ``PYBIND11_NAMESPACE`` instead of using ``pybind11`` when + opening namespaces. Using namespace declarations and namespace qualification + remain the same as ``pybind11``. This is done to ensure consistent symbol + visibility. + `#4098 `_ + +* Mark ``detail::forward_like`` as constexpr. + `#4147 `_ + +* Optimize unpacking_collector when processing ``arg_v`` arguments. + `#4219 `_ + +* Optimize casting C++ object to ``None``. + `#4269 `_ + + +Build system improvements: + +* CMake: revert overwrite behavior, now opt-in with ``PYBIND11_PYTHONLIBS_OVERRWRITE OFF``. + `#4195 `_ + +* Include a pkg-config file when installing pybind11, such as in the Python + package. + `#4077 `_ + +* Avoid stripping debug symbols when ``CMAKE_BUILD_TYPE`` is set to ``DEBUG`` + instead of ``Debug``. + `#4078 `_ + +* Followup to `#3948 `_, fixing vcpkg again. + `#4123 `_ + +Version 2.10.0 (Jul 15, 2022) +----------------------------- + +Removed support for Python 2.7, Python 3.5, and MSVC 2015. Support for MSVC +2017 is limited due to availability of CI runners; we highly recommend MSVC +2019 or 2022 be used. Initial support added for Python 3.11. + +New features: + +* ``py::anyset`` & ``py::frozenset`` were added, with copying (cast) to + ``std::set`` (similar to ``set``). + `#3901 `_ + +* Support bytearray casting to string. + `#3707 `_ + +* ``type_caster`` was added. ``std::monostate`` is a tag type + that allows ``std::variant`` to act as an optional, or allows default + construction of a ``std::variant`` holding a non-default constructible type. + `#3818 `_ + +* ``pybind11::capsule::set_name`` added to mutate the name of the capsule instance. + `#3866 `_ + +* NumPy: dtype constructor from type number added, accessors corresponding to + Python API ``dtype.num``, ``dtype.byteorder``, ``dtype.flags`` and + ``dtype.alignment`` added. + `#3868 `_ + + +Changes: + +* Python 3.6 is now the minimum supported version. + `#3688 `_ + `#3719 `_ + +* The minimum version for MSVC is now 2017. + `#3722 `_ + +* Fix issues with CPython 3.11 betas and add to supported test matrix. + `#3923 `_ + +* ``error_already_set`` is now safer and more performant, especially for + exceptions with long tracebacks, by delaying computation. + `#1895 `_ + +* Improve exception handling in python ``str`` bindings. + `#3826 `_ + +* The bindings for capsules now have more consistent exception handling. + `#3825 `_ + +* ``PYBIND11_OBJECT_CVT`` and ``PYBIND11_OBJECT_CVT_DEFAULT`` macro can now be + used to define classes in namespaces other than pybind11. + `#3797 `_ + +* Error printing code now uses ``PYBIND11_DETAILED_ERROR_MESSAGES`` instead of + requiring ``NDEBUG``, allowing use with release builds if desired. + `#3913 `_ + +* Implicit conversion of the literal ``0`` to ``pybind11::handle`` is now disabled. + `#4008 `_ + + +Bug fixes: + +* Fix exception handling when ``pybind11::weakref()`` fails. + `#3739 `_ + +* ``module_::def_submodule`` was missing proper error handling. This is fixed now. + `#3973 `_ + +* The behavior or ``error_already_set`` was made safer and the highly opaque + "Unknown internal error occurred" message was replaced with a more helpful + message. + `#3982 `_ + +* ``error_already_set::what()`` now handles non-normalized exceptions correctly. + `#3971 `_ + +* Support older C++ compilers where filesystem is not yet part of the standard + library and is instead included in ``std::experimental::filesystem``. + `#3840 `_ + +* Fix ``-Wfree-nonheap-object`` warnings produced by GCC by avoiding returning + pointers to static objects with ``return_value_policy::take_ownership``. + `#3946 `_ + +* Fix cast from pytype rvalue to another pytype. + `#3949 `_ + +* Ensure proper behavior when garbage collecting classes with dynamic attributes in Python >=3.9. + `#4051 `_ + +* A couple long-standing ``PYBIND11_NAMESPACE`` + ``__attribute__((visibility("hidden")))`` inconsistencies are now fixed + (affects only unusual environments). + `#4043 `_ + +* ``pybind11::detail::get_internals()`` is now resilient to in-flight Python + exceptions. + `#3981 `_ + +* Arrays with a dimension of size 0 are now properly converted to dynamic Eigen + matrices (more common in NumPy 1.23). + `#4038 `_ + +* Avoid catching unrelated errors when importing NumPy. + `#3974 `_ + +Performance and style: + +* Added an accessor overload of ``(object &&key)`` to reference steal the + object when using python types as keys. This prevents unnecessary reference + count overhead for attr, dictionary, tuple, and sequence look ups. Added + additional regression tests. Fixed a performance bug the caused accessor + assignments to potentially perform unnecessary copies. + `#3970 `_ + +* Perfect forward all args of ``make_iterator``. + `#3980 `_ + +* Avoid potential bug in pycapsule destructor by adding an ``error_guard`` to + one of the dtors. + `#3958 `_ + +* Optimize dictionary access in ``strip_padding`` for numpy. + `#3994 `_ + +* ``stl_bind.h`` bindings now take slice args as a const-ref. + `#3852 `_ + +* Made slice constructor more consistent, and improve performance of some + casters by allowing reference stealing. + `#3845 `_ + +* Change numpy dtype from_args method to use const ref. + `#3878 `_ + +* Follow rule of three to ensure ``PyErr_Restore`` is called only once. + `#3872 `_ + +* Added missing perfect forwarding for ``make_iterator`` functions. + `#3860 `_ + +* Optimize c++ to python function casting by using the rvalue caster. + `#3966 `_ + +* Optimize Eigen sparse matrix casting by removing unnecessary temporary. + `#4064 `_ + +* Avoid potential implicit copy/assignment constructors causing double free in + ``strdup_gaurd``. + `#3905 `_ + +* Enable clang-tidy checks ``misc-definitions-in-headers``, + ``modernize-loop-convert``, and ``modernize-use-nullptr``. + `#3881 `_ + `#3988 `_ + + +Build system improvements: + +* CMake: Fix file extension on Windows with cp36 and cp37 using FindPython. + `#3919 `_ + +* CMake: Support multiple Python targets (such as on vcpkg). + `#3948 `_ + +* CMake: Fix issue with NVCC on Windows. + `#3947 `_ + +* CMake: Drop the bitness check on cross compiles (like targeting WebAssembly + via Emscripten). + `#3959 `_ + +* Add MSVC builds in debug mode to CI. + `#3784 `_ + +* MSVC 2022 C++20 coverage was added to GitHub Actions, including Eigen. + `#3732 `_, + `#3741 `_ + + +Backend and tidying up: + +* New theme for the documentation. + `#3109 `_ + +* Remove idioms in code comments. Use more inclusive language. + `#3809 `_ + +* ``#include `` was removed from the ``pybind11/stl.h`` header. Your + project may break if it has a transitive dependency on this include. The fix + is to "Include What You Use". + `#3928 `_ + +* Avoid ``setup.py `` usage in internal tests. + `#3734 `_ + + +Version 2.9.2 (Mar 29, 2022) +---------------------------- + +Changes: + +* Enum now has an ``__index__`` method on Python <3.8 too. + `#3700 `_ + +* Local internals are now cleared after finalizing the interpreter. + `#3744 `_ + +Bug fixes: + +* Better support for Python 3.11 alphas. + `#3694 `_ + +* ``PYBIND11_TYPE_CASTER`` now uses fully qualified symbols, so it can be used + outside of ``pybind11::detail``. + `#3758 `_ + +* Some fixes for PyPy 3.9. + `#3768 `_ + +* Fixed a potential memleak in PyPy in ``get_type_override``. + `#3774 `_ + +* Fix usage of ``VISIBILITY_INLINES_HIDDEN``. + `#3721 `_ + + +Build system improvements: + +* Uses ``sysconfig`` module to determine installation locations on Python >= + 3.10, instead of ``distutils`` which has been deprecated. + `#3764 `_ + +* Support Catch 2.13.5+ (supporting GLIBC 2.34+). + `#3679 `_ + +* Fix test failures with numpy 1.22 by ignoring whitespace when comparing + ``str()`` of dtypes. + `#3682 `_ + + +Backend and tidying up: + +* clang-tidy: added ``readability-qualified-auto``, + ``readability-braces-around-statements``, + ``cppcoreguidelines-prefer-member-initializer``, + ``clang-analyzer-optin.performance.Padding``, + ``cppcoreguidelines-pro-type-static-cast-downcast``, and + ``readability-inconsistent-declaration-parameter-name``. + `#3702 `_, + `#3699 `_, + `#3716 `_, + `#3709 `_ + +* clang-format was added to the pre-commit actions, and the entire code base + automatically reformatted (after several iterations preparing for this leap). + `#3713 `_ + + +Version 2.9.1 (Feb 2, 2022) +--------------------------- + +Changes: + +* If possible, attach Python exception with ``py::raise_from`` to ``TypeError`` + when casting from C++ to Python. This will give additional info if Python + exceptions occur in the caster. Adds a test case of trying to convert a set + from C++ to Python when the hash function is not defined in Python. + `#3605 `_ + +* Add a mapping of C++11 nested exceptions to their Python exception + equivalent using ``py::raise_from``. This attaches the nested exceptions in + Python using the ``__cause__`` field. + `#3608 `_ + +* Propagate Python exception traceback using ``raise_from`` if a pybind11 + function runs out of overloads. + `#3671 `_ + +* ``py::multiple_inheritance`` is now only needed when C++ bases are hidden + from pybind11. + `#3650 `_ and + `#3659 `_ + + +Bug fixes: + +* Remove a boolean cast in ``numpy.h`` that causes MSVC C4800 warnings when + compiling against Python 3.10 or newer. + `#3669 `_ + +* Render ``py::bool_`` and ``py::float_`` as ``bool`` and ``float`` + respectively. + `#3622 `_ + +Build system improvements: + +* Fix CMake extension suffix computation on Python 3.10+. + `#3663 `_ + +* Allow ``CMAKE_ARGS`` to override CMake args in pybind11's own ``setup.py``. + `#3577 `_ + +* Remove a few deprecated c-headers. + `#3610 `_ + +* More uniform handling of test targets. + `#3590 `_ + +* Add clang-tidy readability check to catch potentially swapped function args. + `#3611 `_ + + +Version 2.9.0 (Dec 28, 2021) +---------------------------- + +This is the last version to support Python 2.7 and 3.5. + +New Features: + +* Allow ``py::args`` to be followed by other arguments; the remaining arguments + are implicitly keyword-only, as if a ``py::kw_only{}`` annotation had been + used. + `#3402 `_ + +Changes: + +* Make str/bytes/memoryview more interoperable with ``std::string_view``. + `#3521 `_ + +* Replace ``_`` with ``const_name`` in internals, avoid defining ``pybind::_`` + if ``_`` defined as macro (common gettext usage) + `#3423 `_ + + +Bug fixes: + +* Fix a rare warning about extra copy in an Eigen constructor. + `#3486 `_ + +* Fix caching of the C++ overrides. + `#3465 `_ + +* Add missing ``std::forward`` calls to some ``cpp_function`` overloads. + `#3443 `_ + +* Support PyPy 7.3.7 and the PyPy3.8 beta. Test python-3.11 on PRs with the + ``python dev`` label. + `#3419 `_ + +* Replace usage of deprecated ``Eigen::MappedSparseMatrix`` with + ``Eigen::Map>`` for Eigen 3.3+. + `#3499 `_ + +* Tweaks to support Microsoft Visual Studio 2022. + `#3497 `_ + +Build system improvements: + +* Nicer CMake printout and IDE organisation for pybind11's own tests. + `#3479 `_ + +* CMake: report version type as part of the version string to avoid a spurious + space in the package status message. + `#3472 `_ + +* Flags starting with ``-g`` in ``$CFLAGS`` and ``$CPPFLAGS`` are no longer + overridden by ``.Pybind11Extension``. + `#3436 `_ + +* Ensure ThreadPool is closed in ``setup_helpers``. + `#3548 `_ + +* Avoid LTS on ``mips64`` and ``ppc64le`` (reported broken). + `#3557 `_ + + v2.8.1 (Oct 27, 2021) --------------------- @@ -680,7 +1288,7 @@ Packaging / building improvements: `#2338 `_ and `#2370 `_ - * Full integration with CMake’s C++ standard system and compile features + * Full integration with CMake's C++ standard system and compile features replaces ``PYBIND11_CPP_STANDARD``. * Generated config file is now portable to different Python/compiler/CMake diff --git a/ext/pybind11/docs/classes.rst b/ext/pybind11/docs/classes.rst index 13fa8b5387..c0c53135b8 100644 --- a/ext/pybind11/docs/classes.rst +++ b/ext/pybind11/docs/classes.rst @@ -48,10 +48,10 @@ interactive Python session demonstrating this example is shown below: >>> print(p) >>> p.getName() - u'Molly' + 'Molly' >>> p.setName("Charly") >>> p.getName() - u'Charly' + 'Charly' .. seealso:: @@ -124,10 +124,10 @@ This makes it possible to write >>> p = example.Pet("Molly") >>> p.name - u'Molly' + 'Molly' >>> p.name = "Charly" >>> p.name - u'Charly' + 'Charly' Now suppose that ``Pet::name`` was a private internal variable that can only be accessed via setters and getters. @@ -282,9 +282,9 @@ expose fields and methods of both types: >>> p = example.Dog("Molly") >>> p.name - u'Molly' + 'Molly' >>> p.bark() - u'woof!' + 'woof!' The C++ classes defined above are regular non-polymorphic types with an inheritance relationship. This is reflected in Python: @@ -332,7 +332,7 @@ will automatically recognize this: >>> type(p) PolymorphicDog # automatically downcast >>> p.bark() - u'woof!' + 'woof!' Given a pointer to a polymorphic base, pybind11 performs automatic downcasting to the actual derived type. Note that this goes beyond the usual situation in @@ -434,8 +434,7 @@ you can use ``py::detail::overload_cast_impl`` with an additional set of parenth .def("set", overload_cast_()(&Pet::set), "Set the pet's age") .def("set", overload_cast_()(&Pet::set), "Set the pet's name"); -.. [#cpp14] A compiler which supports the ``-std=c++14`` flag - or Visual Studio 2015 Update 2 and newer. +.. [#cpp14] A compiler which supports the ``-std=c++14`` flag. .. note:: @@ -483,7 +482,7 @@ The binding code for this example looks as follows: .value("Cat", Pet::Kind::Cat) .export_values(); - py::class_ attributes(pet, "Attributes") + py::class_(pet, "Attributes") .def(py::init<>()) .def_readwrite("age", &Pet::Attributes::age); diff --git a/ext/pybind11/docs/compiling.rst b/ext/pybind11/docs/compiling.rst index 75608bd576..2b543be0be 100644 --- a/ext/pybind11/docs/compiling.rst +++ b/ext/pybind11/docs/compiling.rst @@ -417,10 +417,10 @@ existing targets instead: .. code-block:: cmake - cmake_minimum_required(VERSION 3.15...3.19) + cmake_minimum_required(VERSION 3.15...3.22) project(example LANGUAGES CXX) - find_package(Python COMPONENTS Interpreter Development REQUIRED) + find_package(Python 3.6 COMPONENTS Interpreter Development REQUIRED) find_package(pybind11 CONFIG REQUIRED) # or add_subdirectory(pybind11) @@ -433,9 +433,8 @@ algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``. .. warning:: - If you use FindPython2 and FindPython3 to dual-target Python, use the - individual targets listed below, and avoid targets that directly include - Python parts. + If you use FindPython to multi-target Python versions, use the individual + targets listed below, and avoid targets that directly include Python parts. There are `many ways to hint or force a discovery of a specific Python installation `_), @@ -462,11 +461,8 @@ available in all modes. The targets provided are: ``pybind11::headers`` Just the pybind11 headers and minimum compile requirements - ``pybind11::python2_no_register`` - Quiets the warning/error when mixing C++14 or higher and Python 2 - ``pybind11::pybind11`` - Python headers + ``pybind11::headers`` + ``pybind11::python2_no_register`` (Python 2 only) + Python headers + ``pybind11::headers`` ``pybind11::python_link_helper`` Just the "linking" part of pybind11:module @@ -475,7 +471,7 @@ available in all modes. The targets provided are: Everything for extension modules - ``pybind11::pybind11`` + ``Python::Module`` (FindPython CMake 3.15+) or ``pybind11::python_link_helper`` ``pybind11::embed`` - Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Embed`` (FindPython) or Python libs + Everything for embedding the Python interpreter - ``pybind11::pybind11`` + ``Python::Python`` (FindPython) or Python libs ``pybind11::lto`` / ``pybind11::thin_lto`` An alternative to `INTERPROCEDURAL_OPTIMIZATION` for adding link-time optimization. @@ -509,7 +505,10 @@ You can use these targets to build complex applications. For example, the target_link_libraries(example PRIVATE pybind11::module pybind11::lto pybind11::windows_extras) pybind11_extension(example) - pybind11_strip(example) + if(NOT MSVC AND NOT ${CMAKE_BUILD_TYPE} MATCHES Debug|RelWithDebInfo) + # Strip unnecessary sections of the binary on Linux/macOS + pybind11_strip(example) + endif() set_target_properties(example PROPERTIES CXX_VISIBILITY_PRESET "hidden" CUDA_VISIBILITY_PRESET "hidden") @@ -577,21 +576,12 @@ On Linux, you can compile an example such as the one given in $ c++ -O3 -Wall -shared -std=c++11 -fPIC $(python3 -m pybind11 --includes) example.cpp -o example$(python3-config --extension-suffix) -The flags given here assume that you're using Python 3. For Python 2, just -change the executable appropriately (to ``python`` or ``python2``). - The ``python3 -m pybind11 --includes`` command fetches the include paths for both pybind11 and Python headers. This assumes that pybind11 has been installed using ``pip`` or ``conda``. If it hasn't, you can also manually specify ``-I /include`` together with the Python includes path ``python3-config --includes``. -Note that Python 2.7 modules don't use a special suffix, so you should simply -use ``example.so`` instead of ``example$(python3-config --extension-suffix)``. -Besides, the ``--extension-suffix`` option may or may not be available, depending -on the distribution; in the latter case, the module extension can be manually -set to ``.so``. - On macOS: the build command is almost the same but it also requires passing the ``-undefined dynamic_lookup`` flag so as to ignore missing symbols when building the module: diff --git a/ext/pybind11/docs/conf.py b/ext/pybind11/docs/conf.py index 092e274e09..2da6773f4f 100644 --- a/ext/pybind11/docs/conf.py +++ b/ext/pybind11/docs/conf.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- # # pybind11 documentation build configuration file, created by # sphinx-quickstart on Sun Oct 11 19:23:48 2015. @@ -36,6 +35,7 @@ DIR = Path(__file__).parent.resolve() # ones. extensions = [ "breathe", + "sphinx_copybutton", "sphinxcontrib.rsvgconverter", "sphinxcontrib.moderncmakedomain", ] @@ -126,23 +126,7 @@ todo_include_todos = False # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -on_rtd = os.environ.get("READTHEDOCS", None) == "True" - -if not on_rtd: # only import and set the theme if we're building docs locally - import sphinx_rtd_theme - - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] - - html_context = {"css_files": ["_static/theme_overrides.css"]} -else: - html_context = { - "css_files": [ - "//media.readthedocs.org/css/sphinx_rtd_theme.css", - "//media.readthedocs.org/css/readthedocs-doc-embed.css", - "_static/theme_overrides.css", - ] - } +html_theme = "furo" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -173,6 +157,10 @@ else: # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] +html_css_files = [ + "css/custom.css", +] + # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. @@ -345,9 +333,9 @@ def generate_doxygen_xml(app): subprocess.call(["doxygen", "--version"]) retcode = subprocess.call(["doxygen"], cwd=app.confdir) if retcode < 0: - sys.stderr.write("doxygen error code: {}\n".format(-retcode)) + sys.stderr.write(f"doxygen error code: {-retcode}\n") except OSError as e: - sys.stderr.write("doxygen execution failed: {}\n".format(e)) + sys.stderr.write(f"doxygen execution failed: {e}\n") def prepare(app): diff --git a/ext/pybind11/docs/faq.rst b/ext/pybind11/docs/faq.rst index e2f477b1f5..28498e7dfc 100644 --- a/ext/pybind11/docs/faq.rst +++ b/ext/pybind11/docs/faq.rst @@ -8,9 +8,7 @@ Frequently asked questions filename of the extension library (without suffixes such as ``.so``). 2. If the above did not fix the issue, you are likely using an incompatible -version of Python (for instance, the extension library was compiled against -Python 2, while the interpreter is running on top of some version of Python -3, or vice versa). +version of Python that does not match what you compiled with. "Symbol not found: ``__Py_ZeroStruct`` / ``_PyInstanceMethod_Type``" ======================================================================== @@ -147,7 +145,7 @@ using C++14 template metaprogramming. .. _`faq:hidden_visibility`: -"‘SomeClass’ declared with greater visibility than the type of its field ‘SomeClass::member’ [-Wattributes]" +"'SomeClass' declared with greater visibility than the type of its field 'SomeClass::member' [-Wattributes]" ============================================================================================================ This error typically indicates that you are compiling without the required @@ -222,20 +220,6 @@ In addition to decreasing binary size, ``-fvisibility=hidden`` also avoids potential serious issues when loading multiple modules and is required for proper pybind operation. See the previous FAQ entry for more details. -Working with ancient Visual Studio 2008 builds on Windows -========================================================= - -The official Windows distributions of Python are compiled using truly -ancient versions of Visual Studio that lack good C++11 support. Some users -implicitly assume that it would be impossible to load a plugin built with -Visual Studio 2015 into a Python distribution that was compiled using Visual -Studio 2008. However, no such issue exists: it's perfectly legitimate to -interface DLLs that are built with different compilers and/or C libraries. -Common gotchas to watch out for involve not ``free()``-ing memory region -that that were ``malloc()``-ed in another shared library, using data -structures with incompatible ABIs, and so on. pybind11 is very careful not -to make these types of mistakes. - How can I properly handle Ctrl-C in long-running functions? =========================================================== @@ -289,27 +273,7 @@ Conflicts can arise, however, when using pybind11 in a project that *also* uses the CMake Python detection in a system with several Python versions installed. This difference may cause inconsistencies and errors if *both* mechanisms are -used in the same project. Consider the following CMake code executed in a -system with Python 2.7 and 3.x installed: - -.. code-block:: cmake - - find_package(PythonInterp) - find_package(PythonLibs) - find_package(pybind11) - -It will detect Python 2.7 and pybind11 will pick it as well. - -In contrast this code: - -.. code-block:: cmake - - find_package(pybind11) - find_package(PythonInterp) - find_package(PythonLibs) - -will detect Python 3.x for pybind11 and may crash on -``find_package(PythonLibs)`` afterwards. +used in the same project. There are three possible solutions: diff --git a/ext/pybind11/docs/pybind11-logo.png b/ext/pybind11/docs/pybind11-logo.png index 4cbad54f79..2d633a4d0c 100644 Binary files a/ext/pybind11/docs/pybind11-logo.png and b/ext/pybind11/docs/pybind11-logo.png differ diff --git a/ext/pybind11/docs/release.rst b/ext/pybind11/docs/release.rst index b5de60f4e5..e761cdf7a6 100644 --- a/ext/pybind11/docs/release.rst +++ b/ext/pybind11/docs/release.rst @@ -22,6 +22,9 @@ the version just below. To release a new version of pybind11: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +If you don't have nox, you should either use ``pipx run nox`` instead, or use +``pipx install nox`` or ``brew install nox`` (Unix). + - Update the version number - Update ``PYBIND11_VERSION_MAJOR`` etc. in ``include/pybind11/detail/common.h``. PATCH should be a simple integer. @@ -51,14 +54,12 @@ To release a new version of pybind11: notifications to users watching releases, and also uploads PyPI packages). (Note: if you do not use an existing tag, this creates a new lightweight tag for you, so you could skip the above step.) - - GUI method: Under `releases `_ click "Draft a new release" on the far right, fill in the tag name (if you didn't tag above, it will be made here), fill in a release name like "Version X.Y.Z", and copy-and-paste the markdown-formatted (!) changelog into the description (usually ``cat docs/changelog.rst | pandoc -f rst -t gfm``). Check "pre-release" if this is a beta/RC. - - CLI method: with ``gh`` installed, run ``gh release create vX.Y.Z -t "Version X.Y.Z"`` If this is a pre-release, add ``-p``. @@ -90,9 +91,7 @@ If you need to manually upload releases, you can download the releases from the .. code-block:: bash - python3 -m pip install build - python3 -m build - PYBIND11_SDIST_GLOBAL=1 python3 -m build + nox -s build twine upload dist/* This makes SDists and wheels, and the final line uploads them. diff --git a/ext/pybind11/docs/requirements.txt b/ext/pybind11/docs/requirements.txt index 8f293b5d34..d2a9ae1645 100644 --- a/ext/pybind11/docs/requirements.txt +++ b/ext/pybind11/docs/requirements.txt @@ -1,8 +1,6 @@ -breathe==4.26.1 -# docutils 0.17 breaks HTML tags & RTD theme -# https://github.com/sphinx-doc/sphinx/issues/9001 -docutils==0.16 -sphinx==3.3.1 -sphinx_rtd_theme==0.5.0 -sphinxcontrib-moderncmakedomain==3.17 -sphinxcontrib-svg2pdfconverter==1.1.0 +breathe==4.34.0 +furo==2022.6.21 +sphinx==5.0.2 +sphinx-copybutton==0.5.0 +sphinxcontrib-moderncmakedomain==3.21.4 +sphinxcontrib-svg2pdfconverter==1.2.0 diff --git a/ext/pybind11/docs/upgrade.rst b/ext/pybind11/docs/upgrade.rst index 69609ca284..6a9db2d08f 100644 --- a/ext/pybind11/docs/upgrade.rst +++ b/ext/pybind11/docs/upgrade.rst @@ -17,6 +17,10 @@ v2.9 converted to using ``py::module_::import("types").attr("SimpleNamespace")`` instead. +* The use of ``_`` in custom type casters can now be replaced with the more + readable ``const_name`` instead. The old ``_`` shortcut has been retained + unless it is being used as a macro (like for gettext). + .. _upgrade-guide-2.7: @@ -520,7 +524,7 @@ include a declaration of the form: PYBIND11_DECLARE_HOLDER_TYPE(T, std::shared_ptr) -Continuing to do so won’t cause an error or even a deprecation warning, +Continuing to do so won't cause an error or even a deprecation warning, but it's completely redundant. diff --git a/ext/pybind11/include/pybind11/attr.h b/ext/pybind11/include/pybind11/attr.h index 0dedbc08dd..b5e3b7b22c 100644 --- a/ext/pybind11/include/pybind11/attr.h +++ b/ext/pybind11/include/pybind11/attr.h @@ -10,6 +10,7 @@ #pragma once +#include "detail/common.h" #include "cast.h" #include @@ -20,65 +21,72 @@ PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) /// @{ /// Annotation for methods -struct is_method { handle class_; +struct is_method { + handle class_; explicit is_method(const handle &c) : class_(c) {} }; /// Annotation for operators -struct is_operator { }; +struct is_operator {}; /// Annotation for classes that cannot be subclassed -struct is_final { }; +struct is_final {}; /// Annotation for parent scope -struct scope { handle value; +struct scope { + handle value; explicit scope(const handle &s) : value(s) {} }; /// Annotation for documentation -struct doc { const char *value; +struct doc { + const char *value; explicit doc(const char *value) : value(value) {} }; /// Annotation for function names -struct name { const char *value; +struct name { + const char *value; explicit name(const char *value) : value(value) {} }; /// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { handle value; +struct sibling { + handle value; explicit sibling(const handle &value) : value(value.ptr()) {} }; /// Annotation indicating that a class derives from another given type -template struct base { +template +struct base { - PYBIND11_DEPRECATED("base() was deprecated in favor of specifying 'T' as a template argument to class_") - base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute + PYBIND11_DEPRECATED( + "base() was deprecated in favor of specifying 'T' as a template argument to class_") + base() = default; }; /// Keep patient alive while nurse lives -template struct keep_alive { }; +template +struct keep_alive {}; /// Annotation indicating that a class is involved in a multiple inheritance relationship -struct multiple_inheritance { }; +struct multiple_inheritance {}; /// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class -struct dynamic_attr { }; +struct dynamic_attr {}; /// Annotation which enables the buffer protocol for a type -struct buffer_protocol { }; +struct buffer_protocol {}; /// Annotation which requests that a special metaclass is created for a type struct metaclass { handle value; PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.") - // NOLINTNEXTLINE(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute - metaclass() {} + metaclass() = default; /// Override pybind11's default metaclass - explicit metaclass(handle value) : value(value) { } + explicit metaclass(handle value) : value(value) {} }; /// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that @@ -99,15 +107,16 @@ struct custom_type_setup { }; /// Annotation that marks a class as local to the module: -struct module_local { const bool value; +struct module_local { + const bool value; constexpr explicit module_local(bool v = true) : value(v) {} }; /// Annotation to mark enums as an arithmetic type -struct arithmetic { }; +struct arithmetic {}; /// Mark a function for addition at the beginning of the existing overload chain instead of the end -struct prepend { }; +struct prepend {}; /** \rst A call policy which places one or more guard variables (``Ts...``) around the function call. @@ -127,9 +136,13 @@ struct prepend { }; return foo(args...); // forwarded arguments }); \endrst */ -template struct call_guard; +template +struct call_guard; -template <> struct call_guard<> { using type = detail::void_type; }; +template <> +struct call_guard<> { + using type = detail::void_type; +}; template struct call_guard { @@ -154,7 +167,8 @@ PYBIND11_NAMESPACE_BEGIN(detail) enum op_id : int; enum op_type : int; struct undefined_t; -template struct op_; +template +struct op_; void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret); /// Internal data structure which holds metadata about a keyword argument @@ -166,15 +180,16 @@ struct argument_record { bool none : 1; ///< True if None is allowed when loading argument_record(const char *name, const char *descr, handle value, bool convert, bool none) - : name(name), descr(descr), value(value), convert(convert), none(none) { } + : name(name), descr(descr), value(value), convert(convert), none(none) {} }; -/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +/// Internal data structure which holds metadata about a bound function (signature, overloads, +/// etc.) struct function_record { function_record() : is_constructor(false), is_new_style_constructor(false), is_stateless(false), - is_operator(false), is_method(false), has_args(false), - has_kwargs(false), has_kw_only_args(false), prepend(false) { } + is_operator(false), is_method(false), has_args(false), has_kwargs(false), + prepend(false) {} /// Function name char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ @@ -189,13 +204,13 @@ struct function_record { std::vector args; /// Pointer to lambda function which converts arguments and performs the actual call - handle (*impl) (function_call &) = nullptr; + handle (*impl)(function_call &) = nullptr; /// Storage for the wrapped function pointer and captured data, if any - void *data[3] = { }; + void *data[3] = {}; /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (function_record *ptr) = nullptr; + void (*free_data)(function_record *ptr) = nullptr; /// Return value policy associated with this function return_value_policy policy = return_value_policy::automatic; @@ -221,17 +236,15 @@ struct function_record { /// True if the function has a '**kwargs' argument bool has_kwargs : 1; - /// True once a 'py::kw_only' is encountered (any following args are keyword-only) - bool has_kw_only_args : 1; - /// True if this function is to be inserted at the beginning of the overload resolution chain bool prepend : 1; /// Number of arguments (including py::args and/or py::kwargs, if present) std::uint16_t nargs; - /// Number of trailing arguments (counted in `nargs`) that are keyword-only - std::uint16_t nargs_kw_only = 0; + /// Number of leading positional arguments, which are terminated by a py::args or py::kwargs + /// argument or by a py::kw_only annotation. + std::uint16_t nargs_pos = 0; /// Number of leading arguments (counted in `nargs`) that are positional-only std::uint16_t nargs_pos_only = 0; @@ -253,7 +266,7 @@ struct function_record { struct type_record { PYBIND11_NOINLINE type_record() : multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false), - default_holder(true), module_local(false), is_final(false) { } + default_holder(true), module_local(false), is_final(false) {} /// Handle to the parent scope handle scope; @@ -312,42 +325,45 @@ struct type_record { /// Is the class inheritable from python classes? bool is_final : 1; - PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *)) { - auto base_info = detail::get_type_info(base, false); + PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) { + auto *base_info = detail::get_type_info(base, false); if (!base_info) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + - "\" referenced unknown base type \"" + tname + "\""); + pybind11_fail("generic_type: type \"" + std::string(name) + + "\" referenced unknown base type \"" + tname + "\""); } if (default_holder != base_info->default_holder) { std::string tname(base.name()); detail::clean_type_id(tname); - pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + - (default_holder ? "does not have" : "has") + - " a non-default holder type while its base \"" + tname + "\" " + - (base_info->default_holder ? "does not" : "does")); + pybind11_fail("generic_type: type \"" + std::string(name) + "\" " + + (default_holder ? "does not have" : "has") + + " a non-default holder type while its base \"" + tname + "\" " + + (base_info->default_holder ? "does not" : "does")); } bases.append((PyObject *) base_info->type); - if (base_info->type->tp_dictoffset != 0) - dynamic_attr = true; +#if PY_VERSION_HEX < 0x030B0000 + dynamic_attr |= base_info->type->tp_dictoffset != 0; +#else + dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0; +#endif - if (caster) + if (caster) { base_info->implicit_casts.emplace_back(type, caster); + } } }; -inline function_call::function_call(const function_record &f, handle p) : - func(f), parent(p) { +inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) { args.reserve(f.nargs); args_convert.reserve(f.nargs); } /// Tag for a new-style `__init__` defined in `detail/init.h` -struct is_new_style_constructor { }; +struct is_new_style_constructor {}; /** * Partial template specializations to process custom attributes provided to @@ -355,129 +371,177 @@ struct is_new_style_constructor { }; * fields in the type_record and function_record data structures or executed at * runtime to deal with custom call policies (e.g. keep_alive). */ -template struct process_attribute; +template +struct process_attribute; -template struct process_attribute_default { +template +struct process_attribute_default { /// Default implementation: do nothing - static void init(const T &, function_record *) { } - static void init(const T &, type_record *) { } - static void precall(function_call &) { } - static void postcall(function_call &, handle) { } + static void init(const T &, function_record *) {} + static void init(const T &, type_record *) {} + static void precall(function_call &) {} + static void postcall(function_call &, handle) {} }; /// Process an attribute specifying the function's name -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } }; /// Process an attribute specifying the function's docstring (provided as a C-style string) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const char *d, function_record *r) { r->doc = const_cast(d); } - static void init(const char *d, type_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = d; } }; -template <> struct process_attribute : process_attribute { }; +template <> +struct process_attribute : process_attribute {}; /// Process an attribute indicating the function's return value policy -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const return_value_policy &p, function_record *r) { r->policy = p; } }; -/// Process an attribute which indicates that this is an overloaded function associated with a given sibling -template <> struct process_attribute : process_attribute_default { +/// Process an attribute which indicates that this is an overloaded function associated with a +/// given sibling +template <> +struct process_attribute : process_attribute_default { static void init(const sibling &s, function_record *r) { r->sibling = s.value; } }; /// Process an attribute which indicates that this function is a method -template <> struct process_attribute : process_attribute_default { - static void init(const is_method &s, function_record *r) { r->is_method = true; r->scope = s.class_; } +template <> +struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { + r->is_method = true; + r->scope = s.class_; + } }; /// Process an attribute which indicates the parent scope of a method -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const scope &s, function_record *r) { r->scope = s.value; } }; /// Process an attribute which indicates that this function is an operator -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const is_operator &, function_record *r) { r->is_operator = true; } }; -template <> struct process_attribute : process_attribute_default { - static void init(const is_new_style_constructor &, function_record *r) { r->is_new_style_constructor = true; } +template <> +struct process_attribute + : process_attribute_default { + static void init(const is_new_style_constructor &, function_record *r) { + r->is_new_style_constructor = true; + } }; -inline void process_kw_only_arg(const arg &a, function_record *r) { - if (!a.name || a.name[0] == '\0') - pybind11_fail("arg(): cannot specify an unnamed argument after an kw_only() annotation"); - ++r->nargs_kw_only; +inline void check_kw_only_arg(const arg &a, function_record *r) { + if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) { + pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or " + "args() argument"); + } +} + +inline void append_self_arg_if_needed(function_record *r) { + if (r->is_method && r->args.empty()) { + r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false); + } } /// Process a keyword argument attribute (*without* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr, handle(), true /*convert*/, false /*none not allowed*/); + append_self_arg_if_needed(r); r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kw_only_arg(a, r); + check_kw_only_arg(a, r); } }; /// Process a keyword argument attribute (*with* a default value) -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const arg_v &a, function_record *r) { - if (r->is_method && r->args.empty()) - r->args.emplace_back("self", nullptr /*descr*/, handle() /*parent*/, true /*convert*/, false /*none not allowed*/); + if (r->is_method && r->args.empty()) { + r->args.emplace_back( + "self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false); + } if (!a.value) { -#if !defined(NDEBUG) +#if defined(PYBIND11_DETAILED_ERROR_MESSAGES) std::string descr("'"); - if (a.name) descr += std::string(a.name) + ": "; + if (a.name) { + descr += std::string(a.name) + ": "; + } descr += a.type + "'"; if (r->is_method) { - if (r->name) - descr += " in method '" + (std::string) str(r->scope) + "." + (std::string) r->name + "'"; - else + if (r->name) { + descr += " in method '" + (std::string) str(r->scope) + "." + + (std::string) r->name + "'"; + } else { descr += " in method of '" + (std::string) str(r->scope) + "'"; + } } else if (r->name) { descr += " in function '" + (std::string) r->name + "'"; } - pybind11_fail("arg(): could not convert default argument " - + descr + " into a Python object (type not registered yet?)"); + pybind11_fail("arg(): could not convert default argument " + descr + + " into a Python object (type not registered yet?)"); #else pybind11_fail("arg(): could not convert default argument " "into a Python object (type not registered yet?). " - "Compile in debug mode for more information."); + "#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for " + "more information."); #endif } r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none); - if (r->has_kw_only_args) process_kw_only_arg(a, r); + check_kw_only_arg(a, r); } }; /// Process a keyword-only-arguments-follow pseudo argument -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const kw_only &, function_record *r) { - r->has_kw_only_args = true; + append_self_arg_if_needed(r); + if (r->has_args && r->nargs_pos != static_cast(r->args.size())) { + pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative " + "argument location (or omit kw_only() entirely)"); + } + r->nargs_pos = static_cast(r->args.size()); } }; /// Process a positional-only-argument maker -template <> struct process_attribute : process_attribute_default { +template <> +struct process_attribute : process_attribute_default { static void init(const pos_only &, function_record *r) { + append_self_arg_if_needed(r); r->nargs_pos_only = static_cast(r->args.size()); + if (r->nargs_pos_only > r->nargs_pos) { + pybind11_fail("pos_only(): cannot follow a py::args() argument"); + } + // It also can't follow a kw_only, but a static_assert in pybind11.h checks that } }; -/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees that) +/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees +/// that) template -struct process_attribute::value>> : process_attribute_default { +struct process_attribute::value>> + : process_attribute_default { static void init(const handle &h, type_record *r) { r->bases.append(h); } }; @@ -490,7 +554,9 @@ struct process_attribute> : process_attribute_default> { /// Process a multiple inheritance attribute template <> struct process_attribute : process_attribute_default { - static void init(const multiple_inheritance &, type_record *r) { r->multiple_inheritance = true; } + static void init(const multiple_inheritance &, type_record *r) { + r->multiple_inheritance = true; + } }; template <> @@ -536,34 +602,41 @@ template <> struct process_attribute : process_attribute_default {}; template -struct process_attribute> : process_attribute_default> { }; +struct process_attribute> : process_attribute_default> {}; /** * Process a keep_alive call policy -- invokes keep_alive_impl during the * pre-call handler if both Nurse, Patient != 0 and use the post-call handler * otherwise */ -template struct process_attribute> : public process_attribute_default> { +template +struct process_attribute> + : public process_attribute_default> { template = 0> - static void precall(function_call &call) { keep_alive_impl(Nurse, Patient, call, handle()); } + static void precall(function_call &call) { + keep_alive_impl(Nurse, Patient, call, handle()); + } template = 0> - static void postcall(function_call &, handle) { } + static void postcall(function_call &, handle) {} template = 0> - static void precall(function_call &) { } + static void precall(function_call &) {} template = 0> - static void postcall(function_call &call, handle ret) { keep_alive_impl(Nurse, Patient, call, ret); } + static void postcall(function_call &call, handle ret) { + keep_alive_impl(Nurse, Patient, call, ret); + } }; /// Recursively iterate over variadic template arguments -template struct process_attributes { - static void init(const Args&... args, function_record *r) { +template +struct process_attributes { + static void init(const Args &...args, function_record *r) { PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); using expander = int[]; (void) expander{ 0, ((void) process_attribute::type>::init(args, r), 0)...}; } - static void init(const Args&... args, type_record *r) { + static void init(const Args &...args, type_record *r) { PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r); PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r); using expander = int[]; @@ -595,7 +668,7 @@ using extract_guard_t = typename exactly_one_t, Extr /// Check the number of named arguments at compile time template ::value...), - size_t self = constexpr_sum(std::is_same::value...)> + size_t self = constexpr_sum(std::is_same::value...)> constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) { PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs); return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs; diff --git a/ext/pybind11/include/pybind11/buffer_info.h b/ext/pybind11/include/pybind11/buffer_info.h index eba68d1aa1..06120d5563 100644 --- a/ext/pybind11/include/pybind11/buffer_info.h +++ b/ext/pybind11/include/pybind11/buffer_info.h @@ -19,9 +19,11 @@ PYBIND11_NAMESPACE_BEGIN(detail) inline std::vector c_strides(const std::vector &shape, ssize_t itemsize) { auto ndim = shape.size(); std::vector strides(ndim, itemsize); - if (ndim > 0) - for (size_t i = ndim - 1; i > 0; --i) + if (ndim > 0) { + for (size_t i = ndim - 1; i > 0; --i) { strides[i - 1] = strides[i] * shape[i]; + } + } return strides; } @@ -29,8 +31,9 @@ inline std::vector c_strides(const std::vector &shape, ssize_t inline std::vector f_strides(const std::vector &shape, ssize_t itemsize) { auto ndim = shape.size(); std::vector strides(ndim, itemsize); - for (size_t i = 1; i < ndim; ++i) + for (size_t i = 1; i < ndim; ++i) { strides[i] = strides[i - 1] * shape[i - 1]; + } return strides; } @@ -41,55 +44,85 @@ struct buffer_info { void *ptr = nullptr; // Pointer to the underlying storage ssize_t itemsize = 0; // Size of individual items in bytes ssize_t size = 0; // Total number of entries - std::string format; // For homogeneous buffers, this should be set to format_descriptor::format() + std::string format; // For homogeneous buffers, this should be set to + // format_descriptor::format() ssize_t ndim = 0; // Number of dimensions std::vector shape; // Shape of the tensor (1 entry per dimension) - std::vector strides; // Number of bytes between adjacent entries (for each per dimension) + std::vector strides; // Number of bytes between adjacent entries + // (for each per dimension) bool readonly = false; // flag to indicate if the underlying storage may be written to buffer_info() = default; - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), - shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { - if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim), + shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) { + if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) { pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length"); - for (size_t i = 0; i < (size_t) ndim; ++i) + } + for (size_t i = 0; i < (size_t) ndim; ++i) { size *= shape[i]; + } } template - buffer_info(T *ptr, detail::any_container shape_in, detail::any_container strides_in, bool readonly=false) - : buffer_info(private_ctr_tag(), ptr, sizeof(T), format_descriptor::format(), static_cast(shape_in->size()), std::move(shape_in), std::move(strides_in), readonly) { } + buffer_info(T *ptr, + detail::any_container shape_in, + detail::any_container strides_in, + bool readonly = false) + : buffer_info(private_ctr_tag(), + ptr, + sizeof(T), + format_descriptor::format(), + static_cast(shape_in->size()), + std::move(shape_in), + std::move(strides_in), + readonly) {} - buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t size, bool readonly=false) - : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) { } + buffer_info(void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t size, + bool readonly = false) + : buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {} template - buffer_info(T *ptr, ssize_t size, bool readonly=false) - : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) { } + buffer_info(T *ptr, ssize_t size, bool readonly = false) + : buffer_info(ptr, sizeof(T), format_descriptor::format(), size, readonly) {} template - buffer_info(const T *ptr, ssize_t size, bool readonly=true) - : buffer_info(const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) { } + buffer_info(const T *ptr, ssize_t size, bool readonly = true) + : buffer_info( + const_cast(ptr), sizeof(T), format_descriptor::format(), size, readonly) {} explicit buffer_info(Py_buffer *view, bool ownview = true) - : buffer_info(view->buf, view->itemsize, view->format, view->ndim, + : buffer_info( + view->buf, + view->itemsize, + view->format, + view->ndim, {view->shape, view->shape + view->ndim}, /* Though buffer::request() requests PyBUF_STRIDES, ctypes objects * ignore this flag and return a view with NULL strides. * When strides are NULL, build them manually. */ view->strides - ? std::vector(view->strides, view->strides + view->ndim) - : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), + ? std::vector(view->strides, view->strides + view->ndim) + : detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize), (view->readonly != 0)) { + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) this->m_view = view; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) this->ownview = ownview; } buffer_info(const buffer_info &) = delete; - buffer_info& operator=(const buffer_info &) = delete; + buffer_info &operator=(const buffer_info &) = delete; buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); } @@ -108,17 +141,28 @@ struct buffer_info { } ~buffer_info() { - if (m_view && ownview) { PyBuffer_Release(m_view); delete m_view; } + if (m_view && ownview) { + PyBuffer_Release(m_view); + delete m_view; + } } Py_buffer *view() const { return m_view; } Py_buffer *&view() { return m_view; } -private: - struct private_ctr_tag { }; - buffer_info(private_ctr_tag, void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim, - detail::any_container &&shape_in, detail::any_container &&strides_in, bool readonly) - : buffer_info(ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) { } +private: + struct private_ctr_tag {}; + + buffer_info(private_ctr_tag, + void *ptr, + ssize_t itemsize, + const std::string &format, + ssize_t ndim, + detail::any_container &&shape_in, + detail::any_container &&strides_in, + bool readonly) + : buffer_info( + ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {} Py_buffer *m_view = nullptr; bool ownview = false; @@ -126,17 +170,22 @@ private: PYBIND11_NAMESPACE_BEGIN(detail) -template struct compare_buffer_info { - static bool compare(const buffer_info& b) { +template +struct compare_buffer_info { + static bool compare(const buffer_info &b) { return b.format == format_descriptor::format() && b.itemsize == (ssize_t) sizeof(T); } }; -template struct compare_buffer_info::value>> { - static bool compare(const buffer_info& b) { - return (size_t) b.itemsize == sizeof(T) && (b.format == format_descriptor::value || - ((sizeof(T) == sizeof(long)) && b.format == (std::is_unsigned::value ? "L" : "l")) || - ((sizeof(T) == sizeof(size_t)) && b.format == (std::is_unsigned::value ? "N" : "n"))); +template +struct compare_buffer_info::value>> { + static bool compare(const buffer_info &b) { + return (size_t) b.itemsize == sizeof(T) + && (b.format == format_descriptor::value + || ((sizeof(T) == sizeof(long)) + && b.format == (std::is_unsigned::value ? "L" : "l")) + || ((sizeof(T) == sizeof(size_t)) + && b.format == (std::is_unsigned::value ? "N" : "n"))); } }; diff --git a/ext/pybind11/include/pybind11/cast.h b/ext/pybind11/include/pybind11/cast.h index 20fbb32587..3a40460276 100644 --- a/ext/pybind11/include/pybind11/cast.h +++ b/ext/pybind11/include/pybind11/cast.h @@ -10,11 +10,12 @@ #pragma once -#include "pytypes.h" #include "detail/common.h" #include "detail/descr.h" #include "detail/type_caster_base.h" #include "detail/typeid.h" +#include "pytypes.h" + #include #include #include @@ -27,61 +28,57 @@ #include #include -#if defined(PYBIND11_CPP17) -# if defined(__has_include) -# if __has_include() -# define PYBIND11_HAS_STRING_VIEW -# endif -# elif defined(_MSC_VER) -# define PYBIND11_HAS_STRING_VIEW -# endif -#endif -#ifdef PYBIND11_HAS_STRING_VIEW -#include -#endif - -#if defined(__cpp_lib_char8_t) && __cpp_lib_char8_t >= 201811L -# define PYBIND11_HAS_U8STRING -#endif - PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE) + +PYBIND11_WARNING_DISABLE_MSVC(4127) + PYBIND11_NAMESPACE_BEGIN(detail) -template class type_caster : public type_caster_base { }; -template using make_caster = type_caster>; +template +class type_caster : public type_caster_base {}; +template +using make_caster = type_caster>; // Shortcut for calling a caster's `cast_op_type` cast operator for casting a type_caster to a T -template typename make_caster::template cast_op_type cast_op(make_caster &caster) { +template +typename make_caster::template cast_op_type cast_op(make_caster &caster) { return caster.operator typename make_caster::template cast_op_type(); } -template typename make_caster::template cast_op_type::type> +template +typename make_caster::template cast_op_type::type> cast_op(make_caster &&caster) { - return std::move(caster).operator - typename make_caster::template cast_op_type::type>(); + return std::move(caster).operator typename make_caster:: + template cast_op_type::type>(); } -template class type_caster> { +template +class type_caster> { private: using caster_t = make_caster; caster_t subcaster; - using reference_t = type&; - using subcaster_cast_op_type = - typename caster_t::template cast_op_type; + using reference_t = type &; + using subcaster_cast_op_type = typename caster_t::template cast_op_type; + + static_assert( + std::is_same::type &, subcaster_cast_op_type>::value + || std::is_same::value, + "std::reference_wrapper caster requires T to have a caster with an " + "`operator T &()` or `operator const T &()`"); - static_assert(std::is_same::type &, subcaster_cast_op_type>::value || - std::is_same::value, - "std::reference_wrapper caster requires T to have a caster with an " - "`operator T &()` or `operator const T &()`"); public: bool load(handle src, bool convert) { return subcaster.load(src, convert); } static constexpr auto name = caster_t::name; - static handle cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { + static handle + cast(const std::reference_wrapper &src, return_value_policy policy, handle parent) { // It is definitely wrong to take ownership of this pointer, so mask that rvp - if (policy == return_value_policy::take_ownership || policy == return_value_policy::automatic) + if (policy == return_value_policy::take_ownership + || policy == return_value_policy::automatic) { policy = return_value_policy::automatic_reference; + } return caster_t::cast(&src.get(), policy, parent); } - template using cast_op_type = std::reference_wrapper; + template + using cast_op_type = std::reference_wrapper; explicit operator std::reference_wrapper() { return cast_op(subcaster); } }; @@ -91,11 +88,16 @@ protected: \ public: \ static constexpr auto name = py_name; \ - template >::value, int> = 0> \ - static handle cast(T_ *src, return_value_policy policy, handle parent) { \ + template >::value, \ + int> \ + = 0> \ + static ::pybind11::handle cast( \ + T_ *src, ::pybind11::return_value_policy policy, ::pybind11::handle parent) { \ if (!src) \ - return none().release(); \ - if (policy == return_value_policy::take_ownership) { \ + return ::pybind11::none().release(); \ + if (policy == ::pybind11::return_value_policy::take_ownership) { \ auto h = cast(std::move(*src), policy, parent); \ delete src; \ return h; \ @@ -106,31 +108,33 @@ public: operator type &() { return value; } /* NOLINT(bugprone-macro-parentheses) */ \ operator type &&() && { return std::move(value); } /* NOLINT(bugprone-macro-parentheses) */ \ template \ - using cast_op_type = pybind11::detail::movable_cast_op_type + using cast_op_type = ::pybind11::detail::movable_cast_op_type -template using is_std_char_type = any_of< - std::is_same, /* std::string */ +template +using is_std_char_type = any_of, /* std::string */ #if defined(PYBIND11_HAS_U8STRING) - std::is_same, /* std::u8string */ + std::is_same, /* std::u8string */ #endif - std::is_same, /* std::u16string */ - std::is_same, /* std::u32string */ - std::is_same /* std::wstring */ ->; - + std::is_same, /* std::u16string */ + std::is_same, /* std::u32string */ + std::is_same /* std::wstring */ + >; template struct type_caster::value && !is_std_char_type::value>> { using _py_type_0 = conditional_t; - using _py_type_1 = conditional_t::value, _py_type_0, typename std::make_unsigned<_py_type_0>::type>; + using _py_type_1 = conditional_t::value, + _py_type_0, + typename std::make_unsigned<_py_type_0>::type>; using py_type = conditional_t::value, double, _py_type_1>; -public: +public: bool load(handle src, bool convert) { py_type py_value; - if (!src) + if (!src) { return false; + } #if !defined(PYPY_VERSION) auto index_check = [](PyObject *o) { return PyIndex_Check(o); }; @@ -141,25 +145,26 @@ public: #endif if (std::is_floating_point::value) { - if (convert || PyFloat_Check(src.ptr())) + if (convert || PyFloat_Check(src.ptr())) { py_value = (py_type) PyFloat_AsDouble(src.ptr()); - else + } else { return false; + } } else if (PyFloat_Check(src.ptr()) || (!convert && !PYBIND11_LONG_CHECK(src.ptr()) && !index_check(src.ptr()))) { return false; } else { handle src_or_index = src; -#if PY_VERSION_HEX < 0x03080000 + // PyPy: 7.3.7's 3.8 does not implement PyLong_*'s __index__ calls. +#if PY_VERSION_HEX < 0x03080000 || defined(PYPY_VERSION) object index; - if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) + if (!PYBIND11_LONG_CHECK(src.ptr())) { // So: index_check(src.ptr()) index = reinterpret_steal(PyNumber_Index(src.ptr())); if (!index) { PyErr_Clear(); if (!convert) return false; - } - else { + } else { src_or_index = index; } } @@ -168,8 +173,8 @@ public: py_value = as_unsigned(src_or_index.ptr()); } else { // signed integer: py_value = sizeof(T) <= sizeof(long) - ? (py_type) PyLong_AsLong(src_or_index.ptr()) - : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); + ? (py_type) PyLong_AsLong(src_or_index.ptr()) + : (py_type) PYBIND11_LONG_AS_LONGLONG(src_or_index.ptr()); } } @@ -178,12 +183,14 @@ public: // Check to see if the conversion is valid (integers should match exactly) // Signed/unsigned checks happen elsewhere - if (py_err || (std::is_integral::value && sizeof(py_type) != sizeof(T) && py_value != (py_type) (T) py_value)) { + if (py_err + || (std::is_integral::value && sizeof(py_type) != sizeof(T) + && py_value != (py_type) (T) py_value)) { PyErr_Clear(); if (py_err && convert && (PyNumber_Check(src.ptr()) != 0)) { auto tmp = reinterpret_steal(std::is_floating_point::value - ? PyNumber_Float(src.ptr()) - : PyNumber_Long(src.ptr())); + ? PyNumber_Float(src.ptr()) + : PyNumber_Long(src.ptr())); PyErr_Clear(); return load(tmp, false); } @@ -194,55 +201,67 @@ public: return true; } - template + template static typename std::enable_if::value, handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyFloat_FromDouble((double) src); } - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) <= sizeof(long)), handle>::type + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) <= sizeof(long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PYBIND11_LONG_FROM_SIGNED((long) src); } - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) <= sizeof(unsigned long)), handle>::type + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) <= sizeof(unsigned long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PYBIND11_LONG_FROM_UNSIGNED((unsigned long) src); } - template - static typename std::enable_if::value && std::is_signed::value && (sizeof(U) > sizeof(long)), handle>::type + template + static typename std::enable_if::value && std::is_signed::value + && (sizeof(U) > sizeof(long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyLong_FromLongLong((long long) src); } - template - static typename std::enable_if::value && std::is_unsigned::value && (sizeof(U) > sizeof(unsigned long)), handle>::type + template + static typename std::enable_if::value && std::is_unsigned::value + && (sizeof(U) > sizeof(unsigned long)), + handle>::type cast(U src, return_value_policy /* policy */, handle /* parent */) { return PyLong_FromUnsignedLongLong((unsigned long long) src); } - PYBIND11_TYPE_CASTER(T, _::value>("int", "float")); + PYBIND11_TYPE_CASTER(T, const_name::value>("int", "float")); }; -template struct void_caster { +template +struct void_caster { public: bool load(handle src, bool) { - if (src && src.is_none()) + if (src && src.is_none()) { return true; + } return false; } static handle cast(T, return_value_policy /* policy */, handle /* parent */) { - return none().inc_ref(); + return none().release(); } - PYBIND11_TYPE_CASTER(T, _("None")); + PYBIND11_TYPE_CASTER(T, const_name("None")); }; -template <> class type_caster : public void_caster {}; +template <> +class type_caster : public void_caster {}; -template <> class type_caster : public type_caster { +template <> +class type_caster : public type_caster { public: using type_caster::cast; @@ -262,7 +281,7 @@ public: } /* Check if this is a C++ type */ - auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); + const auto &bases = all_type_info((PyTypeObject *) type::handle_of(h).ptr()); if (bases.size() == 1) { // Only allowing loading from a single-value type value = values_and_holders(reinterpret_cast(h.ptr())).begin()->value_ptr(); return true; @@ -273,24 +292,31 @@ public: } static handle cast(const void *ptr, return_value_policy /* policy */, handle /* parent */) { - if (ptr) + if (ptr) { return capsule(ptr).release(); - return none().inc_ref(); + } + return none().release(); } - template using cast_op_type = void*&; + template + using cast_op_type = void *&; explicit operator void *&() { return value; } - static constexpr auto name = _("capsule"); + static constexpr auto name = const_name("capsule"); + private: void *value = nullptr; }; -template <> class type_caster : public void_caster { }; +template <> +class type_caster : public void_caster {}; -template <> class type_caster { +template <> +class type_caster { public: bool load(handle src, bool convert) { - if (!src) return false; + if (!src) { + return false; + } if (src.ptr() == Py_True) { value = true; return true; @@ -304,22 +330,22 @@ public: Py_ssize_t res = -1; if (src.is_none()) { - res = 0; // None is implicitly converted to False + res = 0; // None is implicitly converted to False } - #if defined(PYPY_VERSION) - // On PyPy, check that "__bool__" (or "__nonzero__" on Python 2.7) attr exists +#if defined(PYPY_VERSION) + // On PyPy, check that "__bool__" attr exists else if (hasattr(src, PYBIND11_BOOL_ATTR)) { res = PyObject_IsTrue(src.ptr()); } - #else +#else // Alternate approach for CPython: this does the same as the above, but optimized // using the CPython API so as to avoid an unneeded attribute lookup. - else if (auto tp_as_number = src.ptr()->ob_type->tp_as_number) { + else if (auto *tp_as_number = src.ptr()->ob_type->tp_as_number) { if (PYBIND11_NB_BOOL(tp_as_number)) { res = (*PYBIND11_NB_BOOL(tp_as_number))(src.ptr()); } } - #endif +#endif if (res == 0 || res == 1) { value = (res != 0); return true; @@ -331,56 +357,43 @@ public: static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { return handle(src ? Py_True : Py_False).inc_ref(); } - PYBIND11_TYPE_CASTER(bool, _("bool")); + PYBIND11_TYPE_CASTER(bool, const_name("bool")); }; // Helper class for UTF-{8,16,32} C++ stl strings: -template struct string_caster { +template +struct string_caster { using CharT = typename StringType::value_type; // Simplify life by being able to assume standard char sizes (the standard only guarantees // minimums, but Python requires exact sizes) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char size != 1"); #if defined(PYBIND11_HAS_U8STRING) - static_assert(!std::is_same::value || sizeof(CharT) == 1, "Unsupported char8_t size != 1"); + static_assert(!std::is_same::value || sizeof(CharT) == 1, + "Unsupported char8_t size != 1"); #endif - static_assert(!std::is_same::value || sizeof(CharT) == 2, "Unsupported char16_t size != 2"); - static_assert(!std::is_same::value || sizeof(CharT) == 4, "Unsupported char32_t size != 4"); + static_assert(!std::is_same::value || sizeof(CharT) == 2, + "Unsupported char16_t size != 2"); + static_assert(!std::is_same::value || sizeof(CharT) == 4, + "Unsupported char32_t size != 4"); // wchar_t can be either 16 bits (Windows) or 32 (everywhere else) static_assert(!std::is_same::value || sizeof(CharT) == 2 || sizeof(CharT) == 4, - "Unsupported wchar_t size != 2/4"); + "Unsupported wchar_t size != 2/4"); static constexpr size_t UTF_N = 8 * sizeof(CharT); bool load(handle src, bool) { -#if PY_MAJOR_VERSION < 3 - object temp; -#endif handle load_src = src; if (!src) { return false; } if (!PyUnicode_Check(load_src.ptr())) { -#if PY_MAJOR_VERSION >= 3 - return load_bytes(load_src); -#else - if (std::is_same::value) { - return load_bytes(load_src); - } - - // The below is a guaranteed failure in Python 3 when PyUnicode_Check returns false - if (!PYBIND11_BYTES_CHECK(load_src.ptr())) - return false; - - temp = reinterpret_steal(PyUnicode_FromObject(load_src.ptr())); - if (!temp) { PyErr_Clear(); return false; } - load_src = temp; -#endif + return load_raw(load_src); } -#if PY_VERSION_HEX >= 0x03030000 - // On Python >= 3.3, for UTF-8 we avoid the need for a temporary `bytes` - // object by using `PyUnicode_AsUTF8AndSize`. - if (PYBIND11_SILENCE_MSVC_C4127(UTF_N == 8)) { + // For UTF-8 we avoid the need for a temporary `bytes` object by using + // `PyUnicode_AsUTF8AndSize`. + if (UTF_N == 8) { Py_ssize_t size = -1; const auto *buffer = reinterpret_cast(PyUnicode_AsUTF8AndSize(load_src.ptr(), &size)); @@ -391,98 +404,135 @@ template struct string_caster { value = StringType(buffer, static_cast(size)); return true; } -#endif - auto utfNbytes = reinterpret_steal(PyUnicode_AsEncodedString( - load_src.ptr(), UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr)); - if (!utfNbytes) { PyErr_Clear(); return false; } + auto utfNbytes + = reinterpret_steal(PyUnicode_AsEncodedString(load_src.ptr(), + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr)); + if (!utfNbytes) { + PyErr_Clear(); + return false; + } - const auto *buffer = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); + const auto *buffer + = reinterpret_cast(PYBIND11_BYTES_AS_STRING(utfNbytes.ptr())); size_t length = (size_t) PYBIND11_BYTES_SIZE(utfNbytes.ptr()) / sizeof(CharT); // Skip BOM for UTF-16/32 - if (PYBIND11_SILENCE_MSVC_C4127(UTF_N > 8)) { + if (UTF_N > 8) { buffer++; length--; } value = StringType(buffer, length); // If we're loading a string_view we need to keep the encoded Python object alive: - if (IsView) + if (IsView) { loader_life_support::add_patient(utfNbytes); + } return true; } - static handle cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { + static handle + cast(const StringType &src, return_value_policy /* policy */, handle /* parent */) { const char *buffer = reinterpret_cast(src.data()); auto nbytes = ssize_t(src.size() * sizeof(CharT)); handle s = decode_utfN(buffer, nbytes); - if (!s) throw error_already_set(); + if (!s) { + throw error_already_set(); + } return s; } - PYBIND11_TYPE_CASTER(StringType, _(PYBIND11_STRING_NAME)); + PYBIND11_TYPE_CASTER(StringType, const_name(PYBIND11_STRING_NAME)); private: static handle decode_utfN(const char *buffer, ssize_t nbytes) { #if !defined(PYPY_VERSION) - return - UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) : - UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) : - PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); + return UTF_N == 8 ? PyUnicode_DecodeUTF8(buffer, nbytes, nullptr) + : UTF_N == 16 ? PyUnicode_DecodeUTF16(buffer, nbytes, nullptr, nullptr) + : PyUnicode_DecodeUTF32(buffer, nbytes, nullptr, nullptr); #else - // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as well), - // so bypass the whole thing by just passing the encoding as a string value, which works properly: - return PyUnicode_Decode(buffer, nbytes, UTF_N == 8 ? "utf-8" : UTF_N == 16 ? "utf-16" : "utf-32", nullptr); + // PyPy segfaults when on PyUnicode_DecodeUTF16 (and possibly on PyUnicode_DecodeUTF32 as + // well), so bypass the whole thing by just passing the encoding as a string value, which + // works properly: + return PyUnicode_Decode(buffer, + nbytes, + UTF_N == 8 ? "utf-8" + : UTF_N == 16 ? "utf-16" + : "utf-32", + nullptr); #endif } - // When loading into a std::string or char*, accept a bytes object as-is (i.e. + // When loading into a std::string or char*, accept a bytes/bytearray object as-is (i.e. // without any encoding/decoding attempt). For other C++ char sizes this is a no-op. // which supports loading a unicode from a str, doesn't take this path. template - bool load_bytes(enable_if_t::value, handle> src) { + bool load_raw(enable_if_t::value, handle> src) { if (PYBIND11_BYTES_CHECK(src.ptr())) { - // We were passed a Python 3 raw bytes; accept it into a std::string or char* + // We were passed raw bytes; accept it into a std::string or char* // without any encoding attempt. const char *bytes = PYBIND11_BYTES_AS_STRING(src.ptr()); - if (bytes) { - value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); - return true; + if (!bytes) { + pybind11_fail("Unexpected PYBIND11_BYTES_AS_STRING() failure."); } + value = StringType(bytes, (size_t) PYBIND11_BYTES_SIZE(src.ptr())); + return true; + } + if (PyByteArray_Check(src.ptr())) { + // We were passed a bytearray; accept it into a std::string or char* + // without any encoding attempt. + const char *bytearray = PyByteArray_AsString(src.ptr()); + if (!bytearray) { + pybind11_fail("Unexpected PyByteArray_AsString() failure."); + } + value = StringType(bytearray, (size_t) PyByteArray_Size(src.ptr())); + return true; } return false; } template - bool load_bytes(enable_if_t::value, handle>) { return false; } + bool load_raw(enable_if_t::value, handle>) { + return false; + } }; template -struct type_caster, enable_if_t::value>> +struct type_caster, + enable_if_t::value>> : string_caster> {}; #ifdef PYBIND11_HAS_STRING_VIEW template -struct type_caster, enable_if_t::value>> +struct type_caster, + enable_if_t::value>> : string_caster, true> {}; #endif // Type caster for C-style strings. We basically use a std::string type caster, but also add the // ability to use None as a nullptr char* (which the string caster doesn't allow). -template struct type_caster::value>> { +template +struct type_caster::value>> { using StringType = std::basic_string; - using StringCaster = type_caster; + using StringCaster = make_caster; StringCaster str_caster; bool none = false; CharT one_char = 0; + public: bool load(handle src, bool convert) { - if (!src) return false; + if (!src) { + return false; + } if (src.is_none()) { // Defer accepting None to other overloads (if we aren't in convert mode): - if (!convert) return false; + if (!convert) { + return false; + } none = true; return true; } @@ -490,14 +540,18 @@ public: } static handle cast(const CharT *src, return_value_policy policy, handle parent) { - if (src == nullptr) return pybind11::none().inc_ref(); + if (src == nullptr) { + return pybind11::none().release(); + } return StringCaster::cast(StringType(src), policy, parent); } static handle cast(CharT src, return_value_policy policy, handle parent) { if (std::is_same::value) { handle s = PyUnicode_DecodeLatin1((const char *) &src, 1, nullptr); - if (!s) throw error_already_set(); + if (!s) { + throw error_already_set(); + } return s; } return StringCaster::cast(StringType(1, src), policy, parent); @@ -507,20 +561,22 @@ public: return none ? nullptr : const_cast(static_cast(str_caster).c_str()); } explicit operator CharT &() { - if (none) + if (none) { throw value_error("Cannot convert None to a character"); + } auto &value = static_cast(str_caster); size_t str_len = value.size(); - if (str_len == 0) + if (str_len == 0) { throw value_error("Cannot convert empty string to a character"); + } // If we're in UTF-8 mode, we have two possible failures: one for a unicode character that - // is too high, and one for multiple unicode characters (caught later), so we need to figure - // out how long the first encoded character is in bytes to distinguish between these two - // errors. We also allow want to allow unicode characters U+0080 through U+00FF, as those - // can fit into a single char value. - if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 8) && str_len > 1 && str_len <= 4) { + // is too high, and one for multiple unicode characters (caught later), so we need to + // figure out how long the first encoded character is in bytes to distinguish between these + // two errors. We also allow want to allow unicode characters U+0080 through U+00FF, as + // those can fit into a single char value. + if (StringCaster::UTF_N == 8 && str_len > 1 && str_len <= 4) { auto v0 = static_cast(value[0]); // low bits only: 0-127 // 0b110xxxxx - start of 2-byte sequence @@ -534,7 +590,8 @@ public: if (char0_bytes == str_len) { // If we have a 128-255 value, we can decode it into a single char: if (char0_bytes == 2 && (v0 & 0xFC) == 0xC0) { // 0x110000xx 0x10xxxxxx - one_char = static_cast(((v0 & 3) << 6) + (static_cast(value[1]) & 0x3F)); + one_char = static_cast(((v0 & 3) << 6) + + (static_cast(value[1]) & 0x3F)); return one_char; } // Otherwise we have a single character, but it's > U+00FF @@ -545,36 +602,42 @@ public: // UTF-16 is much easier: we can only have a surrogate pair for values above U+FFFF, thus a // surrogate pair with total length 2 instantly indicates a range error (but not a "your // string was too long" error). - else if (PYBIND11_SILENCE_MSVC_C4127(StringCaster::UTF_N == 16) && str_len == 2) { + else if (StringCaster::UTF_N == 16 && str_len == 2) { one_char = static_cast(value[0]); - if (one_char >= 0xD800 && one_char < 0xE000) + if (one_char >= 0xD800 && one_char < 0xE000) { throw value_error("Character code point not in range(0x10000)"); + } } - if (str_len != 1) + if (str_len != 1) { throw value_error("Expected a character, but multi-character string found"); + } one_char = value[0]; return one_char; } - static constexpr auto name = _(PYBIND11_STRING_NAME); - template using cast_op_type = pybind11::detail::cast_op_type<_T>; + static constexpr auto name = const_name(PYBIND11_STRING_NAME); + template + using cast_op_type = pybind11::detail::cast_op_type<_T>; }; // Base implementation for std::tuple and std::pair -template class Tuple, typename... Ts> class tuple_caster { +template