Remove spack.target
from code (#46503)
The `spack.target.Target` class is a weird entity, that is just needed to: 1. Sort microarchitectures in lists deterministically 2. Being able to use microarchitectures in hashed containers This PR removes it, and uses `archspec.cpu.Microarchitecture` directly. To sort lists, we use a proper `key=` when needed. Being able to use `Microarchitecture` objects in sets is achieved by updating the external `archspec`. Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
This commit is contained in:
parent
35ae2743d9
commit
b93c57cab9
@ -219,6 +219,7 @@ def setup(sphinx):
|
||||
("py:class", "spack.install_test.Pb"),
|
||||
("py:class", "spack.filesystem_view.SimpleFilesystemView"),
|
||||
("py:class", "spack.traverse.EdgeAndDepth"),
|
||||
("py:class", "archspec.cpu.microarchitecture.Microarchitecture"),
|
||||
]
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
|
2
lib/spack/external/__init__.py
vendored
2
lib/spack/external/__init__.py
vendored
@ -18,7 +18,7 @@
|
||||
|
||||
* Homepage: https://pypi.python.org/pypi/archspec
|
||||
* Usage: Labeling, comparison and detection of microarchitectures
|
||||
* Version: 0.2.5-dev (commit cbb1fd5eb397a70d466e5160b393b87b0dbcc78f)
|
||||
* Version: 0.2.5-dev (commit bceb39528ac49dd0c876b2e9bf3e7482e9c2be4a)
|
||||
|
||||
astunparse
|
||||
----------------
|
||||
|
@ -115,6 +115,9 @@ def __eq__(self, other):
|
||||
and self.cpu_part == other.cpu_part
|
||||
)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.name)
|
||||
|
||||
@coerce_target_names
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
@ -45,6 +45,8 @@
|
||||
from itertools import chain
|
||||
from typing import Dict, List, Set, Tuple
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import llnl.util.tty as tty
|
||||
from llnl.string import plural
|
||||
from llnl.util.filesystem import join_path
|
||||
@ -358,7 +360,7 @@ def set_compiler_environment_variables(pkg, env):
|
||||
_add_werror_handling(keep_werror, env)
|
||||
|
||||
# Set the target parameters that the compiler will add
|
||||
isa_arg = spec.architecture.target.optimization_flags(compiler)
|
||||
isa_arg = optimization_flags(compiler, spec.target)
|
||||
env.set("SPACK_TARGET_ARGS", isa_arg)
|
||||
|
||||
# Trap spack-tracked compiler flags as appropriate.
|
||||
@ -403,6 +405,36 @@ def set_compiler_environment_variables(pkg, env):
|
||||
return env
|
||||
|
||||
|
||||
def optimization_flags(compiler, target):
|
||||
if spack.compilers.is_mixed_toolchain(compiler):
|
||||
msg = (
|
||||
"microarchitecture specific optimizations are not "
|
||||
"supported yet on mixed compiler toolchains [check"
|
||||
f" {compiler.name}@{compiler.version} for further details]"
|
||||
)
|
||||
tty.debug(msg)
|
||||
return ""
|
||||
|
||||
# Try to check if the current compiler comes with a version number or
|
||||
# has an unexpected suffix. If so, treat it as a compiler with a
|
||||
# custom spec.
|
||||
compiler_version = compiler.version
|
||||
version_number, suffix = archspec.cpu.version_components(compiler.version)
|
||||
if not version_number or suffix:
|
||||
try:
|
||||
compiler_version = compiler.real_version
|
||||
except spack.util.executable.ProcessError as e:
|
||||
# log this and just return compiler.version instead
|
||||
tty.debug(str(e))
|
||||
|
||||
try:
|
||||
result = target.optimization_flags(compiler.name, compiler_version.dotted_numeric_string)
|
||||
except (ValueError, archspec.cpu.UnsupportedMicroarchitecture):
|
||||
result = ""
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def set_wrapper_variables(pkg, env):
|
||||
"""Set environment variables used by the Spack compiler wrapper (which have the prefix
|
||||
`SPACK_`) and also add the compiler wrappers to PATH.
|
||||
@ -783,7 +815,6 @@ def setup_package(pkg, dirty, context: Context = Context.BUILD):
|
||||
# Platform specific setup goes before package specific setup. This is for setting
|
||||
# defaults like MACOSX_DEPLOYMENT_TARGET on macOS.
|
||||
platform = spack.platforms.by_name(pkg.spec.architecture.platform)
|
||||
target = platform.target(pkg.spec.architecture.target)
|
||||
platform.setup_platform_environment(pkg, env_mods)
|
||||
|
||||
tty.debug("setup_package: grabbing modifications from dependencies")
|
||||
@ -808,9 +839,6 @@ def setup_package(pkg, dirty, context: Context = Context.BUILD):
|
||||
for mod in pkg.compiler.modules:
|
||||
load_module(mod)
|
||||
|
||||
if target and target.module_name:
|
||||
load_module(target.module_name)
|
||||
|
||||
load_external_modules(pkg)
|
||||
|
||||
implicit_rpaths = pkg.compiler.implicit_rpaths()
|
||||
|
@ -558,7 +558,7 @@ def get_compilers(config, cspec=None, arch_spec=None):
|
||||
except KeyError:
|
||||
# TODO: Check if this exception handling makes sense, or if we
|
||||
# TODO: need to change / refactor tests
|
||||
family = arch_spec.target
|
||||
family = str(arch_spec.target)
|
||||
except AttributeError:
|
||||
assert arch_spec is None
|
||||
|
||||
@ -803,12 +803,11 @@ def _extract_os_and_target(spec: "spack.spec.Spec"):
|
||||
if not spec.architecture:
|
||||
host_platform = spack.platforms.host()
|
||||
operating_system = host_platform.operating_system("default_os")
|
||||
target = host_platform.target("default_target").microarchitecture
|
||||
target = host_platform.target("default_target")
|
||||
else:
|
||||
target = spec.architecture.target
|
||||
if not target:
|
||||
target = spack.platforms.host().target("default_target")
|
||||
target = target.microarchitecture
|
||||
|
||||
operating_system = spec.os
|
||||
if not operating_system:
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
import spack
|
||||
import spack.error
|
||||
import spack.fetch_strategy as fs
|
||||
import spack.fetch_strategy
|
||||
import spack.mirror
|
||||
import spack.repo
|
||||
import spack.stage
|
||||
@ -314,11 +314,15 @@ def stage(self) -> "spack.stage.Stage":
|
||||
|
||||
# Two checksums, one for compressed file, one for its contents
|
||||
if self.archive_sha256 and self.sha256:
|
||||
fetcher: fs.FetchStrategy = fs.FetchAndVerifyExpandedFile(
|
||||
self.url, archive_sha256=self.archive_sha256, expanded_sha256=self.sha256
|
||||
fetcher: spack.fetch_strategy.FetchStrategy = (
|
||||
spack.fetch_strategy.FetchAndVerifyExpandedFile(
|
||||
self.url, archive_sha256=self.archive_sha256, expanded_sha256=self.sha256
|
||||
)
|
||||
)
|
||||
else:
|
||||
fetcher = fs.URLFetchStrategy(url=self.url, sha256=self.sha256, expand=False)
|
||||
fetcher = spack.fetch_strategy.URLFetchStrategy(
|
||||
url=self.url, sha256=self.sha256, expand=False
|
||||
)
|
||||
|
||||
# The same package can have multiple patches with the same name but
|
||||
# with different contents, therefore apply a subset of the hash.
|
||||
@ -397,7 +401,7 @@ def from_dict(
|
||||
sha256 = dictionary["sha256"]
|
||||
checker = Checker(sha256)
|
||||
if patch.path and not checker.check(patch.path):
|
||||
raise fs.ChecksumError(
|
||||
raise spack.fetch_strategy.ChecksumError(
|
||||
"sha256 checksum failed for %s" % patch.path,
|
||||
"Expected %s but got %s " % (sha256, checker.sum)
|
||||
+ "Patch may have changed since concretization.",
|
||||
|
@ -4,6 +4,8 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
from typing import Optional
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import llnl.util.lang
|
||||
|
||||
import spack.error
|
||||
@ -60,7 +62,7 @@ def __init__(self, name):
|
||||
self.operating_sys = {}
|
||||
self.name = name
|
||||
|
||||
def add_target(self, name, target):
|
||||
def add_target(self, name: str, target: archspec.cpu.Microarchitecture) -> None:
|
||||
"""Used by the platform specific subclass to list available targets.
|
||||
Raises an error if the platform specifies a name
|
||||
that is reserved by spack as an alias.
|
||||
@ -70,6 +72,10 @@ def add_target(self, name, target):
|
||||
raise ValueError(msg.format(name))
|
||||
self.targets[name] = target
|
||||
|
||||
def _add_archspec_targets(self):
|
||||
for name, microarchitecture in archspec.cpu.TARGETS.items():
|
||||
self.add_target(name, microarchitecture)
|
||||
|
||||
def target(self, name):
|
||||
"""This is a getter method for the target dictionary
|
||||
that handles defaulting based on the values provided by default,
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import spack.target
|
||||
from spack.operating_systems.mac_os import MacOs
|
||||
from spack.version import Version
|
||||
|
||||
@ -21,9 +20,7 @@ class Darwin(Platform):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("darwin")
|
||||
|
||||
for name in archspec.cpu.TARGETS:
|
||||
self.add_target(name, spack.target.Target(name))
|
||||
self._add_archspec_targets()
|
||||
|
||||
self.default = archspec.cpu.host().name
|
||||
self.front_end = self.default
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import spack.target
|
||||
from spack.operating_systems.freebsd import FreeBSDOs
|
||||
|
||||
from ._platform import Platform
|
||||
@ -18,8 +17,7 @@ class FreeBSD(Platform):
|
||||
def __init__(self):
|
||||
super().__init__("freebsd")
|
||||
|
||||
for name in archspec.cpu.TARGETS:
|
||||
self.add_target(name, spack.target.Target(name))
|
||||
self._add_archspec_targets()
|
||||
|
||||
# Get specific default
|
||||
self.default = archspec.cpu.host().name
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import spack.target
|
||||
from spack.operating_systems.linux_distro import LinuxDistro
|
||||
|
||||
from ._platform import Platform
|
||||
@ -18,8 +17,7 @@ class Linux(Platform):
|
||||
def __init__(self):
|
||||
super().__init__("linux")
|
||||
|
||||
for name in archspec.cpu.TARGETS:
|
||||
self.add_target(name, spack.target.Target(name))
|
||||
self._add_archspec_targets()
|
||||
|
||||
# Get specific default
|
||||
self.default = archspec.cpu.host().name
|
||||
|
@ -4,8 +4,9 @@
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
import platform
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import spack.operating_systems
|
||||
import spack.target
|
||||
|
||||
from ._platform import Platform
|
||||
|
||||
@ -32,8 +33,8 @@ class Test(Platform):
|
||||
def __init__(self, name=None):
|
||||
name = name or "test"
|
||||
super().__init__(name)
|
||||
self.add_target(self.default, spack.target.Target(self.default))
|
||||
self.add_target(self.front_end, spack.target.Target(self.front_end))
|
||||
self.add_target(self.default, archspec.cpu.TARGETS[self.default])
|
||||
self.add_target(self.front_end, archspec.cpu.TARGETS[self.front_end])
|
||||
|
||||
self.add_operating_system(
|
||||
self.default_os, spack.operating_systems.OperatingSystem("debian", 6)
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import spack.target
|
||||
from spack.operating_systems.windows_os import WindowsOs
|
||||
|
||||
from ._platform import Platform
|
||||
@ -18,9 +17,7 @@ class Windows(Platform):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("windows")
|
||||
|
||||
for name in archspec.cpu.TARGETS:
|
||||
self.add_target(name, spack.target.Target(name))
|
||||
self._add_archspec_targets()
|
||||
|
||||
self.default = archspec.cpu.host().name
|
||||
self.front_end = self.default
|
||||
|
@ -2479,7 +2479,7 @@ def _all_targets_satisfiying(single_constraint):
|
||||
return allowed_targets
|
||||
|
||||
cache = {}
|
||||
for target_constraint in sorted(self.target_constraints):
|
||||
for target_constraint in sorted(self.target_constraints, key=lambda x: x.name):
|
||||
# Construct the list of allowed targets for this constraint
|
||||
allowed_targets = []
|
||||
for single_constraint in str(target_constraint).split(","):
|
||||
@ -3237,7 +3237,7 @@ def add_compiler_from_concrete_spec(self, spec: "spack.spec.Spec") -> None:
|
||||
candidate = KnownCompiler(
|
||||
spec=spec.compiler,
|
||||
os=str(spec.architecture.os),
|
||||
target=str(spec.architecture.target.microarchitecture.family),
|
||||
target=str(spec.architecture.target.family),
|
||||
available=False,
|
||||
compiler_obj=None,
|
||||
)
|
||||
|
@ -61,6 +61,8 @@
|
||||
import warnings
|
||||
from typing import Any, Callable, Dict, List, Match, Optional, Set, Tuple, Union
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import llnl.path
|
||||
import llnl.string
|
||||
import llnl.util.filesystem as fs
|
||||
@ -82,7 +84,6 @@
|
||||
import spack.repo
|
||||
import spack.solver
|
||||
import spack.store
|
||||
import spack.target
|
||||
import spack.traverse as traverse
|
||||
import spack.util.executable
|
||||
import spack.util.hash
|
||||
@ -213,6 +214,12 @@ def ensure_modern_format_string(fmt: str) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _make_microarchitecture(name: str) -> archspec.cpu.Microarchitecture:
|
||||
if isinstance(name, archspec.cpu.Microarchitecture):
|
||||
return name
|
||||
return archspec.cpu.TARGETS.get(name, archspec.cpu.generic_microarchitecture(name))
|
||||
|
||||
|
||||
@lang.lazy_lexicographic_ordering
|
||||
class ArchSpec:
|
||||
"""Aggregate the target platform, the operating system and the target microarchitecture."""
|
||||
@ -301,7 +308,10 @@ def _autospec(self, spec_like):
|
||||
def _cmp_iter(self):
|
||||
yield self.platform
|
||||
yield self.os
|
||||
yield self.target
|
||||
if self.target is None:
|
||||
yield self.target
|
||||
else:
|
||||
yield self.target.name
|
||||
|
||||
@property
|
||||
def platform(self):
|
||||
@ -360,10 +370,10 @@ def target(self, value):
|
||||
# will assumed to be the host machine's platform.
|
||||
|
||||
def target_or_none(t):
|
||||
if isinstance(t, spack.target.Target):
|
||||
if isinstance(t, archspec.cpu.Microarchitecture):
|
||||
return t
|
||||
if t and t != "None":
|
||||
return spack.target.Target(t)
|
||||
return _make_microarchitecture(t)
|
||||
return None
|
||||
|
||||
value = target_or_none(value)
|
||||
@ -452,10 +462,11 @@ def _target_constrain(self, other: "ArchSpec") -> bool:
|
||||
results = self._target_intersection(other)
|
||||
attribute_str = ",".join(results)
|
||||
|
||||
if self.target == attribute_str:
|
||||
intersection_target = _make_microarchitecture(attribute_str)
|
||||
if self.target == intersection_target:
|
||||
return False
|
||||
|
||||
self.target = attribute_str
|
||||
self.target = intersection_target
|
||||
return True
|
||||
|
||||
def _target_intersection(self, other):
|
||||
@ -473,7 +484,7 @@ def _target_intersection(self, other):
|
||||
# s_target_range is a concrete target
|
||||
# get a microarchitecture reference for at least one side
|
||||
# of each comparison so we can use archspec comparators
|
||||
s_comp = spack.target.Target(s_min).microarchitecture
|
||||
s_comp = _make_microarchitecture(s_min)
|
||||
if not o_sep:
|
||||
if s_min == o_min:
|
||||
results.append(s_min)
|
||||
@ -481,21 +492,21 @@ def _target_intersection(self, other):
|
||||
results.append(s_min)
|
||||
elif not o_sep:
|
||||
# "cast" to microarchitecture
|
||||
o_comp = spack.target.Target(o_min).microarchitecture
|
||||
o_comp = _make_microarchitecture(o_min)
|
||||
if (not s_min or o_comp >= s_min) and (not s_max or o_comp <= s_max):
|
||||
results.append(o_min)
|
||||
else:
|
||||
# Take intersection of two ranges
|
||||
# Lots of comparisons needed
|
||||
_s_min = spack.target.Target(s_min).microarchitecture
|
||||
_s_max = spack.target.Target(s_max).microarchitecture
|
||||
_o_min = spack.target.Target(o_min).microarchitecture
|
||||
_o_max = spack.target.Target(o_max).microarchitecture
|
||||
_s_min = _make_microarchitecture(s_min)
|
||||
_s_max = _make_microarchitecture(s_max)
|
||||
_o_min = _make_microarchitecture(o_min)
|
||||
_o_max = _make_microarchitecture(o_max)
|
||||
|
||||
n_min = s_min if _s_min >= _o_min else o_min
|
||||
n_max = s_max if _s_max <= _o_max else o_max
|
||||
_n_min = spack.target.Target(n_min).microarchitecture
|
||||
_n_max = spack.target.Target(n_max).microarchitecture
|
||||
_n_min = _make_microarchitecture(n_min)
|
||||
_n_max = _make_microarchitecture(n_max)
|
||||
if _n_min == _n_max:
|
||||
results.append(n_min)
|
||||
elif not n_min or not n_max or _n_min < _n_max:
|
||||
@ -548,12 +559,18 @@ def target_concrete(self):
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
# Generic targets represent either an architecture family (like x86_64)
|
||||
# or a custom micro-architecture
|
||||
if self.target.vendor == "generic":
|
||||
target_data = str(self.target)
|
||||
else:
|
||||
# Get rid of compiler flag information before turning the uarch into a dict
|
||||
uarch_dict = self.target.to_dict()
|
||||
uarch_dict.pop("compilers", None)
|
||||
target_data = syaml.syaml_dict(uarch_dict.items())
|
||||
|
||||
d = syaml.syaml_dict(
|
||||
[
|
||||
("platform", self.platform),
|
||||
("platform_os", self.os),
|
||||
("target", self.target.to_dict_or_value()),
|
||||
]
|
||||
[("platform", self.platform), ("platform_os", self.os), ("target", target_data)]
|
||||
)
|
||||
return syaml.syaml_dict([("arch", d)])
|
||||
|
||||
@ -561,7 +578,10 @@ def to_dict(self):
|
||||
def from_dict(d):
|
||||
"""Import an ArchSpec from raw YAML/JSON data"""
|
||||
arch = d["arch"]
|
||||
target = spack.target.Target.from_dict_or_value(arch["target"])
|
||||
target_name = arch["target"]
|
||||
if not isinstance(target_name, str):
|
||||
target_name = target_name["name"]
|
||||
target = _make_microarchitecture(target_name)
|
||||
return ArchSpec((arch["platform"], arch["platform_os"], target))
|
||||
|
||||
def __str__(self):
|
||||
@ -4120,9 +4140,7 @@ def os(self):
|
||||
|
||||
@property
|
||||
def target(self):
|
||||
# This property returns the underlying microarchitecture object
|
||||
# to give to the attribute the appropriate comparison semantic
|
||||
return self.architecture.target.microarchitecture
|
||||
return self.architecture.target
|
||||
|
||||
@property
|
||||
def build_spec(self):
|
||||
|
@ -33,7 +33,6 @@
|
||||
import spack.caches
|
||||
import spack.config
|
||||
import spack.error
|
||||
import spack.fetch_strategy as fs
|
||||
import spack.mirror
|
||||
import spack.resource
|
||||
import spack.spec
|
||||
@ -43,6 +42,7 @@
|
||||
import spack.util.path as sup
|
||||
import spack.util.pattern as pattern
|
||||
import spack.util.url as url_util
|
||||
from spack import fetch_strategy as fs # breaks a cycle
|
||||
from spack.util.crypto import bit_length, prefix_bits
|
||||
from spack.util.editor import editor, executable
|
||||
from spack.version import StandardVersion, VersionList
|
||||
@ -352,8 +352,8 @@ def __init__(
|
||||
url_or_fetch_strategy,
|
||||
*,
|
||||
name=None,
|
||||
mirror_paths: Optional[spack.mirror.MirrorLayout] = None,
|
||||
mirrors: Optional[Iterable[spack.mirror.Mirror]] = None,
|
||||
mirror_paths: Optional["spack.mirror.MirrorLayout"] = None,
|
||||
mirrors: Optional[Iterable["spack.mirror.Mirror"]] = None,
|
||||
keep=False,
|
||||
path=None,
|
||||
lock=True,
|
||||
@ -464,7 +464,7 @@ def source_path(self):
|
||||
"""Returns the well-known source directory path."""
|
||||
return os.path.join(self.path, _source_path_subdir)
|
||||
|
||||
def _generate_fetchers(self, mirror_only=False) -> Generator[fs.FetchStrategy, None, None]:
|
||||
def _generate_fetchers(self, mirror_only=False) -> Generator["fs.FetchStrategy", None, None]:
|
||||
fetchers: List[fs.FetchStrategy] = []
|
||||
if not mirror_only:
|
||||
fetchers.append(self.default_fetcher)
|
||||
@ -600,7 +600,7 @@ def cache_local(self):
|
||||
spack.caches.FETCH_CACHE.store(self.fetcher, self.mirror_layout.path)
|
||||
|
||||
def cache_mirror(
|
||||
self, mirror: spack.caches.MirrorCache, stats: spack.mirror.MirrorStats
|
||||
self, mirror: "spack.caches.MirrorCache", stats: "spack.mirror.MirrorStats"
|
||||
) -> None:
|
||||
"""Perform a fetch if the resource is not already cached
|
||||
|
||||
@ -668,7 +668,7 @@ def destroy(self):
|
||||
class ResourceStage(Stage):
|
||||
def __init__(
|
||||
self,
|
||||
fetch_strategy: fs.FetchStrategy,
|
||||
fetch_strategy: "fs.FetchStrategy",
|
||||
root: Stage,
|
||||
resource: spack.resource.Resource,
|
||||
**kwargs,
|
||||
|
@ -1,161 +0,0 @@
|
||||
# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
import functools
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
import llnl.util.tty as tty
|
||||
|
||||
import spack.compiler
|
||||
import spack.compilers
|
||||
import spack.spec
|
||||
import spack.util.executable
|
||||
import spack.util.spack_yaml as syaml
|
||||
|
||||
|
||||
def _ensure_other_is_target(method):
|
||||
"""In a single argument method, ensure that the argument is an
|
||||
instance of ``Target``.
|
||||
"""
|
||||
|
||||
@functools.wraps(method)
|
||||
def _impl(self, other):
|
||||
if isinstance(other, str):
|
||||
other = Target(other)
|
||||
|
||||
if not isinstance(other, Target):
|
||||
return NotImplemented
|
||||
|
||||
return method(self, other)
|
||||
|
||||
return _impl
|
||||
|
||||
|
||||
class Target:
|
||||
def __init__(self, name, module_name=None):
|
||||
"""Target models microarchitectures and their compatibility.
|
||||
|
||||
Args:
|
||||
name (str or Microarchitecture): microarchitecture of the target
|
||||
module_name (str): optional module name to get access to the
|
||||
current target. This is typically used on machines
|
||||
like Cray (e.g. craype-compiler)
|
||||
"""
|
||||
if not isinstance(name, archspec.cpu.Microarchitecture):
|
||||
name = archspec.cpu.TARGETS.get(name, archspec.cpu.generic_microarchitecture(name))
|
||||
self.microarchitecture = name
|
||||
self.module_name = module_name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.microarchitecture.name
|
||||
|
||||
@_ensure_other_is_target
|
||||
def __eq__(self, other):
|
||||
return (
|
||||
self.microarchitecture == other.microarchitecture
|
||||
and self.module_name == other.module_name
|
||||
)
|
||||
|
||||
def __ne__(self, other):
|
||||
# This method is necessary as long as we support Python 2. In Python 3
|
||||
# __ne__ defaults to the implementation below
|
||||
return not self == other
|
||||
|
||||
@_ensure_other_is_target
|
||||
def __lt__(self, other):
|
||||
# TODO: In the future it would be convenient to say
|
||||
# TODO: `spec.architecture.target < other.architecture.target`
|
||||
# TODO: and change the semantic of the comparison operators
|
||||
|
||||
# This is needed to sort deterministically specs in a list.
|
||||
# It doesn't implement a total ordering semantic.
|
||||
return self.microarchitecture.name < other.microarchitecture.name
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.name, self.module_name))
|
||||
|
||||
@staticmethod
|
||||
def from_dict_or_value(dict_or_value):
|
||||
# A string here represents a generic target (like x86_64 or ppc64) or
|
||||
# a custom micro-architecture
|
||||
if isinstance(dict_or_value, str):
|
||||
return Target(dict_or_value)
|
||||
|
||||
# TODO: From a dict we actually retrieve much more information than
|
||||
# TODO: just the name. We can use that information to reconstruct an
|
||||
# TODO: "old" micro-architecture or check the current definition.
|
||||
target_info = dict_or_value
|
||||
return Target(target_info["name"])
|
||||
|
||||
def to_dict_or_value(self):
|
||||
"""Returns a dict or a value representing the current target.
|
||||
|
||||
String values are used to keep backward compatibility with generic
|
||||
targets, like e.g. x86_64 or ppc64. More specific micro-architectures
|
||||
will return a dictionary which contains information on the name,
|
||||
features, vendor, generation and parents of the current target.
|
||||
"""
|
||||
# Generic targets represent either an architecture
|
||||
# family (like x86_64) or a custom micro-architecture
|
||||
if self.microarchitecture.vendor == "generic":
|
||||
return str(self)
|
||||
|
||||
# Get rid of compiler flag information before turning the uarch into a dict
|
||||
uarch_dict = self.microarchitecture.to_dict()
|
||||
uarch_dict.pop("compilers", None)
|
||||
return syaml.syaml_dict(uarch_dict.items())
|
||||
|
||||
def __repr__(self):
|
||||
cls_name = self.__class__.__name__
|
||||
fmt = cls_name + "({0}, {1})"
|
||||
return fmt.format(repr(self.microarchitecture), repr(self.module_name))
|
||||
|
||||
def __str__(self):
|
||||
return str(self.microarchitecture)
|
||||
|
||||
def __contains__(self, cpu_flag):
|
||||
return cpu_flag in self.microarchitecture
|
||||
|
||||
def optimization_flags(self, compiler):
|
||||
"""Returns the flags needed to optimize for this target using
|
||||
the compiler passed as argument.
|
||||
|
||||
Args:
|
||||
compiler (spack.spec.CompilerSpec or spack.compiler.Compiler): object that
|
||||
contains both the name and the version of the compiler we want to use
|
||||
"""
|
||||
# Mixed toolchains are not supported yet
|
||||
if isinstance(compiler, spack.compiler.Compiler):
|
||||
if spack.compilers.is_mixed_toolchain(compiler):
|
||||
msg = (
|
||||
"microarchitecture specific optimizations are not "
|
||||
"supported yet on mixed compiler toolchains [check"
|
||||
" {0.name}@{0.version} for further details]"
|
||||
)
|
||||
tty.debug(msg.format(compiler))
|
||||
return ""
|
||||
|
||||
# Try to check if the current compiler comes with a version number or
|
||||
# has an unexpected suffix. If so, treat it as a compiler with a
|
||||
# custom spec.
|
||||
compiler_version = compiler.version
|
||||
version_number, suffix = archspec.cpu.version_components(compiler.version)
|
||||
if not version_number or suffix:
|
||||
# Try to deduce the underlying version of the compiler, regardless
|
||||
# of its name in compilers.yaml. Depending on where this function
|
||||
# is called we might get either a CompilerSpec or a fully fledged
|
||||
# compiler object.
|
||||
if isinstance(compiler, spack.spec.CompilerSpec):
|
||||
compiler = spack.compilers.compilers_for_spec(compiler).pop()
|
||||
try:
|
||||
compiler_version = compiler.real_version
|
||||
except spack.util.executable.ProcessError as e:
|
||||
# log this and just return compiler.version instead
|
||||
tty.debug(str(e))
|
||||
|
||||
return self.microarchitecture.optimization_flags(
|
||||
compiler.name, compiler_version.dotted_numeric_string
|
||||
)
|
@ -12,7 +12,6 @@
|
||||
import spack.concretize
|
||||
import spack.operating_systems
|
||||
import spack.platforms
|
||||
import spack.target
|
||||
from spack.spec import ArchSpec, Spec
|
||||
|
||||
|
||||
@ -83,25 +82,6 @@ def test_operating_system_conversion_to_dict():
|
||||
assert operating_system.to_dict() == {"name": "os", "version": "1.0"}
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"cpu_flag,target_name",
|
||||
[
|
||||
# Test that specific flags can be used in queries
|
||||
("ssse3", "haswell"),
|
||||
("popcnt", "nehalem"),
|
||||
("avx512f", "skylake_avx512"),
|
||||
("avx512ifma", "icelake"),
|
||||
# Test that proxy flags can be used in queries too
|
||||
("sse3", "nehalem"),
|
||||
("avx512", "skylake_avx512"),
|
||||
("avx512", "icelake"),
|
||||
],
|
||||
)
|
||||
def test_target_container_semantic(cpu_flag, target_name):
|
||||
target = spack.target.Target(target_name)
|
||||
assert cpu_flag in target
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"item,architecture_str",
|
||||
[
|
||||
@ -118,68 +98,6 @@ def test_arch_spec_container_semantic(item, architecture_str):
|
||||
assert item in architecture
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"compiler_spec,target_name,expected_flags",
|
||||
[
|
||||
# Homogeneous compilers
|
||||
("gcc@4.7.2", "ivybridge", "-march=core-avx-i -mtune=core-avx-i"),
|
||||
("clang@3.5", "x86_64", "-march=x86-64 -mtune=generic"),
|
||||
("apple-clang@9.1.0", "x86_64", "-march=x86-64"),
|
||||
# Mixed toolchain
|
||||
("clang@8.0.0", "broadwell", ""),
|
||||
],
|
||||
)
|
||||
@pytest.mark.filterwarnings("ignore:microarchitecture specific")
|
||||
@pytest.mark.not_on_windows("Windows doesn't support the compiler wrapper")
|
||||
def test_optimization_flags(compiler_spec, target_name, expected_flags, compiler_factory):
|
||||
target = spack.target.Target(target_name)
|
||||
compiler_dict = compiler_factory(spec=compiler_spec, operating_system="")["compiler"]
|
||||
if compiler_spec == "clang@8.0.0":
|
||||
compiler_dict["paths"] = {
|
||||
"cc": "/path/to/clang-8",
|
||||
"cxx": "/path/to/clang++-8",
|
||||
"f77": "/path/to/gfortran-9",
|
||||
"fc": "/path/to/gfortran-9",
|
||||
}
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_dict)
|
||||
|
||||
opt_flags = target.optimization_flags(compiler)
|
||||
assert opt_flags == expected_flags
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"compiler_str,real_version,target_str,expected_flags",
|
||||
[
|
||||
("gcc@=9.2.0", None, "haswell", "-march=haswell -mtune=haswell"),
|
||||
# Check that custom string versions are accepted
|
||||
("gcc@=10foo", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"),
|
||||
# Check that we run version detection (4.4.0 doesn't support icelake)
|
||||
("gcc@=4.4.0-special", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"),
|
||||
# Check that the special case for Apple's clang is treated correctly
|
||||
# i.e. it won't try to detect the version again
|
||||
("apple-clang@=9.1.0", None, "x86_64", "-march=x86-64"),
|
||||
],
|
||||
)
|
||||
def test_optimization_flags_with_custom_versions(
|
||||
compiler_str,
|
||||
real_version,
|
||||
target_str,
|
||||
expected_flags,
|
||||
monkeypatch,
|
||||
mutable_config,
|
||||
compiler_factory,
|
||||
):
|
||||
target = spack.target.Target(target_str)
|
||||
compiler_dict = compiler_factory(spec=compiler_str, operating_system="redhat6")
|
||||
mutable_config.set("compilers", [compiler_dict])
|
||||
if real_version:
|
||||
monkeypatch.setattr(spack.compiler.Compiler, "get_real_version", lambda x: real_version)
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_dict["compiler"])
|
||||
|
||||
opt_flags = target.optimization_flags(compiler)
|
||||
assert opt_flags == expected_flags
|
||||
|
||||
|
||||
@pytest.mark.regression("15306")
|
||||
@pytest.mark.parametrize(
|
||||
"architecture_tuple,constraint_tuple",
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
import pytest
|
||||
|
||||
import archspec.cpu
|
||||
|
||||
from llnl.path import Path, convert_to_platform_path
|
||||
from llnl.util.filesystem import HeaderList, LibraryList
|
||||
|
||||
@ -737,3 +739,64 @@ def test_rpath_with_duplicate_link_deps():
|
||||
assert child in rpath_deps
|
||||
assert runtime_2 in rpath_deps
|
||||
assert runtime_1 not in rpath_deps
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"compiler_spec,target_name,expected_flags",
|
||||
[
|
||||
# Homogeneous compilers
|
||||
("gcc@4.7.2", "ivybridge", "-march=core-avx-i -mtune=core-avx-i"),
|
||||
("clang@3.5", "x86_64", "-march=x86-64 -mtune=generic"),
|
||||
("apple-clang@9.1.0", "x86_64", "-march=x86-64"),
|
||||
# Mixed toolchain
|
||||
("clang@8.0.0", "broadwell", ""),
|
||||
],
|
||||
)
|
||||
@pytest.mark.filterwarnings("ignore:microarchitecture specific")
|
||||
@pytest.mark.not_on_windows("Windows doesn't support the compiler wrapper")
|
||||
def test_optimization_flags(compiler_spec, target_name, expected_flags, compiler_factory):
|
||||
target = archspec.cpu.TARGETS[target_name]
|
||||
compiler_dict = compiler_factory(spec=compiler_spec, operating_system="")["compiler"]
|
||||
if compiler_spec == "clang@8.0.0":
|
||||
compiler_dict["paths"] = {
|
||||
"cc": "/path/to/clang-8",
|
||||
"cxx": "/path/to/clang++-8",
|
||||
"f77": "/path/to/gfortran-9",
|
||||
"fc": "/path/to/gfortran-9",
|
||||
}
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_dict)
|
||||
opt_flags = spack.build_environment.optimization_flags(compiler, target)
|
||||
assert opt_flags == expected_flags
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"compiler_str,real_version,target_str,expected_flags",
|
||||
[
|
||||
("gcc@=9.2.0", None, "haswell", "-march=haswell -mtune=haswell"),
|
||||
# Check that custom string versions are accepted
|
||||
("gcc@=10foo", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"),
|
||||
# Check that we run version detection (4.4.0 doesn't support icelake)
|
||||
("gcc@=4.4.0-special", "9.2.0", "icelake", "-march=icelake-client -mtune=icelake-client"),
|
||||
# Check that the special case for Apple's clang is treated correctly
|
||||
# i.e. it won't try to detect the version again
|
||||
("apple-clang@=9.1.0", None, "x86_64", "-march=x86-64"),
|
||||
],
|
||||
)
|
||||
def test_optimization_flags_with_custom_versions(
|
||||
compiler_str,
|
||||
real_version,
|
||||
target_str,
|
||||
expected_flags,
|
||||
monkeypatch,
|
||||
mutable_config,
|
||||
compiler_factory,
|
||||
):
|
||||
target = archspec.cpu.TARGETS[target_str]
|
||||
compiler_dict = compiler_factory(spec=compiler_str, operating_system="redhat6")
|
||||
mutable_config.set("compilers", [compiler_dict])
|
||||
if real_version:
|
||||
monkeypatch.setattr(spack.compiler.Compiler, "get_real_version", lambda x: real_version)
|
||||
compiler = spack.compilers.compiler_from_dict(compiler_dict["compiler"])
|
||||
|
||||
opt_flags = spack.build_environment.optimization_flags(compiler, target)
|
||||
assert opt_flags == expected_flags
|
||||
|
@ -407,7 +407,7 @@ def test_substitute_config_variables(mock_low_high_config, monkeypatch):
|
||||
) == os.path.abspath(os.path.join("foo", "test", "bar"))
|
||||
|
||||
host_target = spack.platforms.host().target("default_target")
|
||||
host_target_family = str(host_target.microarchitecture.family)
|
||||
host_target_family = str(host_target.family)
|
||||
assert spack_path.canonicalize_path(
|
||||
os.path.join("foo", "$target_family", "bar")
|
||||
) == os.path.abspath(os.path.join("foo", host_target_family, "bar"))
|
||||
|
@ -71,7 +71,7 @@ def replacements():
|
||||
"operating_system": lambda: arch.os,
|
||||
"os": lambda: arch.os,
|
||||
"target": lambda: arch.target,
|
||||
"target_family": lambda: arch.target.microarchitecture.family,
|
||||
"target_family": lambda: arch.target.family,
|
||||
"date": lambda: date.today().strftime("%Y-%m-%d"),
|
||||
"env": lambda: ev.active_environment().path if ev.active_environment() else NOMATCH,
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
from llnl.util import tty
|
||||
|
||||
from spack.build_environment import optimization_flags
|
||||
from spack.package import *
|
||||
from spack.pkg.builtin.fftw import FftwBase
|
||||
|
||||
@ -213,10 +214,7 @@ def configure(self, spec, prefix):
|
||||
# variable to set AMD_ARCH configure option.
|
||||
# Spack user can not directly use AMD_ARCH for this purpose but should
|
||||
# use target variable to set appropriate -march option in AMD_ARCH.
|
||||
arch = spec.architecture
|
||||
options.append(
|
||||
"AMD_ARCH={0}".format(arch.target.optimization_flags(spec.compiler).split("=")[-1])
|
||||
)
|
||||
options.append(f"AMD_ARCH={optimization_flags(self.compiler, spec.target)}")
|
||||
|
||||
# Specific SIMD support.
|
||||
# float and double precisions are supported
|
||||
|
@ -2,7 +2,7 @@
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack.build_environment import optimization_flags
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@ -110,21 +110,23 @@ def build_targets(self):
|
||||
return ["all", "html"] if "+doc" in self.spec else ["all"]
|
||||
|
||||
def cmake_args(self):
|
||||
spec = self.spec
|
||||
args = [
|
||||
self.define_from_variant("ARB_WITH_ASSERTIONS", "assertions"),
|
||||
self.define_from_variant("ARB_WITH_MPI", "mpi"),
|
||||
self.define_from_variant("ARB_WITH_PYTHON", "python"),
|
||||
self.define_from_variant("ARB_VECTORIZE", "vectorize"),
|
||||
self.define("ARB_ARCH", "none"),
|
||||
self.define("ARB_CXX_FLAGS_TARGET", optimization_flags(self.compiler, spec.target)),
|
||||
]
|
||||
|
||||
if self.spec.satisfies("+cuda"):
|
||||
args.append("-DARB_GPU=cuda")
|
||||
args.append(self.define_from_variant("ARB_USE_GPU_RNG", "gpu_rng"))
|
||||
|
||||
# query spack for the architecture-specific compiler flags set by its wrapper
|
||||
args.append("-DARB_ARCH=none")
|
||||
opt_flags = self.spec.architecture.target.optimization_flags(self.spec.compiler)
|
||||
args.append("-DARB_CXX_FLAGS_TARGET=" + opt_flags)
|
||||
args.extend(
|
||||
[
|
||||
self.define("ARB_GPU", "cuda"),
|
||||
self.define_from_variant("ARB_USE_GPU_RNG", "gpu_rng"),
|
||||
]
|
||||
)
|
||||
|
||||
return args
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
import platform
|
||||
import re
|
||||
|
||||
from spack.build_environment import optimization_flags
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@ -161,7 +162,7 @@ def edit(self, spec, prefix):
|
||||
if spec.satisfies("%intel"):
|
||||
# with intel-parallel-studio+mpi the '-march' arguments
|
||||
# are not passed to icc
|
||||
arch_opt = spec.architecture.target.optimization_flags(spec.compiler)
|
||||
arch_opt = optimization_flags(self.compiler, spec.target)
|
||||
self.config["@CCFLAGS@"] = f"-O3 -restrict -ansi-alias -ip {arch_opt}"
|
||||
self.config["@CCNOOPT@"] = "-restrict"
|
||||
self._write_make_arch(spec, prefix)
|
||||
|
@ -5,6 +5,7 @@
|
||||
import datetime as dt
|
||||
import os
|
||||
|
||||
from spack.build_environment import optimization_flags
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@ -898,7 +899,7 @@ def cmake_args(self):
|
||||
args.append(self.define("CMAKE_CXX_FLAGS_RELWITHDEBINFO", cxx_flags))
|
||||
|
||||
# Overwrite generic cpu tune option
|
||||
cmake_tune_flags = spec.architecture.target.optimization_flags(spec.compiler)
|
||||
cmake_tune_flags = optimization_flags(self.compiler, spec.target)
|
||||
args.append(self.define("CMAKE_TUNE_FLAGS", cmake_tune_flags))
|
||||
|
||||
args.append(self.define_from_variant("LAMMPS_SIZES", "lammps_sizes"))
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
import llnl.util.tty as tty
|
||||
|
||||
from spack.build_environment import optimization_flags
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@ -175,7 +176,7 @@ def _edit_arch_generic(self, spec, prefix):
|
||||
# this options are take from the default provided
|
||||
# configuration files
|
||||
# https://github.com/UIUC-PPL/charm/pull/2778
|
||||
archopt = spec.architecture.target.optimization_flags(spec.compiler)
|
||||
archopt = optimization_flags(self.compiler, spec.target)
|
||||
|
||||
if self.spec.satisfies("^charmpp@:6.10.1"):
|
||||
optims_opts = {
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack.build_environment import optimization_flags
|
||||
from spack.package import *
|
||||
|
||||
|
||||
@ -149,7 +150,7 @@ def cmake_args(self):
|
||||
|
||||
# add cpu arch specific optimisation flags to CMake so that they are passed
|
||||
# to embedded Makefile that neuron has for compiling MOD files
|
||||
compilation_flags = self.spec.architecture.target.optimization_flags(self.spec.compiler)
|
||||
compilation_flags = optimization_flags(self.compiler, self.spec.target)
|
||||
args.append(self.define("CMAKE_CXX_FLAGS", compilation_flags))
|
||||
|
||||
return args
|
||||
|
@ -8,6 +8,7 @@
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from spack.build_environment import optimization_flags
|
||||
from spack.package import *
|
||||
|
||||
rocm_dependencies = [
|
||||
@ -656,7 +657,7 @@ def setup_build_environment(self, env):
|
||||
|
||||
# Please specify optimization flags to use during compilation when
|
||||
# bazel option '--config=opt' is specified
|
||||
env.set("CC_OPT_FLAGS", spec.architecture.target.optimization_flags(spec.compiler))
|
||||
env.set("CC_OPT_FLAGS", optimization_flags(self.compiler, spec.target))
|
||||
|
||||
# Would you like to interactively configure ./WORKSPACE for
|
||||
# Android builds?
|
||||
|
@ -83,7 +83,7 @@ def determine_variants(cls, libs, ver_str):
|
||||
return variants
|
||||
|
||||
def _spec_arch_to_sdk_arch(self):
|
||||
spec_arch = str(self.spec.architecture.target.microarchitecture.family).lower()
|
||||
spec_arch = str(self.spec.architecture.target.family).lower()
|
||||
_64bit = "64" in spec_arch
|
||||
arm = "arm" in spec_arch
|
||||
if arm:
|
||||
|
Loading…
Reference in New Issue
Block a user