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:
Massimiliano Culpo 2024-09-21 14:05:41 +02:00 committed by GitHub
parent 35ae2743d9
commit b93c57cab9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 201 additions and 326 deletions

View File

@ -219,6 +219,7 @@ def setup(sphinx):
("py:class", "spack.install_test.Pb"), ("py:class", "spack.install_test.Pb"),
("py:class", "spack.filesystem_view.SimpleFilesystemView"), ("py:class", "spack.filesystem_view.SimpleFilesystemView"),
("py:class", "spack.traverse.EdgeAndDepth"), ("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. # The reST default role (used for this markup: `text`) to use for all documents.

View File

@ -18,7 +18,7 @@
* Homepage: https://pypi.python.org/pypi/archspec * Homepage: https://pypi.python.org/pypi/archspec
* Usage: Labeling, comparison and detection of microarchitectures * Usage: Labeling, comparison and detection of microarchitectures
* Version: 0.2.5-dev (commit cbb1fd5eb397a70d466e5160b393b87b0dbcc78f) * Version: 0.2.5-dev (commit bceb39528ac49dd0c876b2e9bf3e7482e9c2be4a)
astunparse astunparse
---------------- ----------------

View File

@ -115,6 +115,9 @@ def __eq__(self, other):
and self.cpu_part == other.cpu_part and self.cpu_part == other.cpu_part
) )
def __hash__(self):
return hash(self.name)
@coerce_target_names @coerce_target_names
def __ne__(self, other): def __ne__(self, other):
return not self == other return not self == other

View File

@ -45,6 +45,8 @@
from itertools import chain from itertools import chain
from typing import Dict, List, Set, Tuple from typing import Dict, List, Set, Tuple
import archspec.cpu
import llnl.util.tty as tty import llnl.util.tty as tty
from llnl.string import plural from llnl.string import plural
from llnl.util.filesystem import join_path from llnl.util.filesystem import join_path
@ -358,7 +360,7 @@ def set_compiler_environment_variables(pkg, env):
_add_werror_handling(keep_werror, env) _add_werror_handling(keep_werror, env)
# Set the target parameters that the compiler will add # 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) env.set("SPACK_TARGET_ARGS", isa_arg)
# Trap spack-tracked compiler flags as appropriate. # Trap spack-tracked compiler flags as appropriate.
@ -403,6 +405,36 @@ def set_compiler_environment_variables(pkg, env):
return 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): def set_wrapper_variables(pkg, env):
"""Set environment variables used by the Spack compiler wrapper (which have the prefix """Set environment variables used by the Spack compiler wrapper (which have the prefix
`SPACK_`) and also add the compiler wrappers to PATH. `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 # Platform specific setup goes before package specific setup. This is for setting
# defaults like MACOSX_DEPLOYMENT_TARGET on macOS. # defaults like MACOSX_DEPLOYMENT_TARGET on macOS.
platform = spack.platforms.by_name(pkg.spec.architecture.platform) platform = spack.platforms.by_name(pkg.spec.architecture.platform)
target = platform.target(pkg.spec.architecture.target)
platform.setup_platform_environment(pkg, env_mods) platform.setup_platform_environment(pkg, env_mods)
tty.debug("setup_package: grabbing modifications from dependencies") 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: for mod in pkg.compiler.modules:
load_module(mod) load_module(mod)
if target and target.module_name:
load_module(target.module_name)
load_external_modules(pkg) load_external_modules(pkg)
implicit_rpaths = pkg.compiler.implicit_rpaths() implicit_rpaths = pkg.compiler.implicit_rpaths()

View File

@ -558,7 +558,7 @@ def get_compilers(config, cspec=None, arch_spec=None):
except KeyError: except KeyError:
# TODO: Check if this exception handling makes sense, or if we # TODO: Check if this exception handling makes sense, or if we
# TODO: need to change / refactor tests # TODO: need to change / refactor tests
family = arch_spec.target family = str(arch_spec.target)
except AttributeError: except AttributeError:
assert arch_spec is None assert arch_spec is None
@ -803,12 +803,11 @@ def _extract_os_and_target(spec: "spack.spec.Spec"):
if not spec.architecture: if not spec.architecture:
host_platform = spack.platforms.host() host_platform = spack.platforms.host()
operating_system = host_platform.operating_system("default_os") operating_system = host_platform.operating_system("default_os")
target = host_platform.target("default_target").microarchitecture target = host_platform.target("default_target")
else: else:
target = spec.architecture.target target = spec.architecture.target
if not target: if not target:
target = spack.platforms.host().target("default_target") target = spack.platforms.host().target("default_target")
target = target.microarchitecture
operating_system = spec.os operating_system = spec.os
if not operating_system: if not operating_system:

View File

@ -15,7 +15,7 @@
import spack import spack
import spack.error import spack.error
import spack.fetch_strategy as fs import spack.fetch_strategy
import spack.mirror import spack.mirror
import spack.repo import spack.repo
import spack.stage import spack.stage
@ -314,11 +314,15 @@ def stage(self) -> "spack.stage.Stage":
# Two checksums, one for compressed file, one for its contents # Two checksums, one for compressed file, one for its contents
if self.archive_sha256 and self.sha256: if self.archive_sha256 and self.sha256:
fetcher: fs.FetchStrategy = fs.FetchAndVerifyExpandedFile( fetcher: spack.fetch_strategy.FetchStrategy = (
self.url, archive_sha256=self.archive_sha256, expanded_sha256=self.sha256 spack.fetch_strategy.FetchAndVerifyExpandedFile(
self.url, archive_sha256=self.archive_sha256, expanded_sha256=self.sha256
)
) )
else: 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 # The same package can have multiple patches with the same name but
# with different contents, therefore apply a subset of the hash. # with different contents, therefore apply a subset of the hash.
@ -397,7 +401,7 @@ def from_dict(
sha256 = dictionary["sha256"] sha256 = dictionary["sha256"]
checker = Checker(sha256) checker = Checker(sha256)
if patch.path and not checker.check(patch.path): if patch.path and not checker.check(patch.path):
raise fs.ChecksumError( raise spack.fetch_strategy.ChecksumError(
"sha256 checksum failed for %s" % patch.path, "sha256 checksum failed for %s" % patch.path,
"Expected %s but got %s " % (sha256, checker.sum) "Expected %s but got %s " % (sha256, checker.sum)
+ "Patch may have changed since concretization.", + "Patch may have changed since concretization.",

View File

@ -4,6 +4,8 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
from typing import Optional from typing import Optional
import archspec.cpu
import llnl.util.lang import llnl.util.lang
import spack.error import spack.error
@ -60,7 +62,7 @@ def __init__(self, name):
self.operating_sys = {} self.operating_sys = {}
self.name = name 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. """Used by the platform specific subclass to list available targets.
Raises an error if the platform specifies a name Raises an error if the platform specifies a name
that is reserved by spack as an alias. that is reserved by spack as an alias.
@ -70,6 +72,10 @@ def add_target(self, name, target):
raise ValueError(msg.format(name)) raise ValueError(msg.format(name))
self.targets[name] = target 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): def target(self, name):
"""This is a getter method for the target dictionary """This is a getter method for the target dictionary
that handles defaulting based on the values provided by default, that handles defaulting based on the values provided by default,

View File

@ -7,7 +7,6 @@
import archspec.cpu import archspec.cpu
import spack.target
from spack.operating_systems.mac_os import MacOs from spack.operating_systems.mac_os import MacOs
from spack.version import Version from spack.version import Version
@ -21,9 +20,7 @@ class Darwin(Platform):
def __init__(self): def __init__(self):
super().__init__("darwin") super().__init__("darwin")
self._add_archspec_targets()
for name in archspec.cpu.TARGETS:
self.add_target(name, spack.target.Target(name))
self.default = archspec.cpu.host().name self.default = archspec.cpu.host().name
self.front_end = self.default self.front_end = self.default

View File

@ -6,7 +6,6 @@
import archspec.cpu import archspec.cpu
import spack.target
from spack.operating_systems.freebsd import FreeBSDOs from spack.operating_systems.freebsd import FreeBSDOs
from ._platform import Platform from ._platform import Platform
@ -18,8 +17,7 @@ class FreeBSD(Platform):
def __init__(self): def __init__(self):
super().__init__("freebsd") super().__init__("freebsd")
for name in archspec.cpu.TARGETS: self._add_archspec_targets()
self.add_target(name, spack.target.Target(name))
# Get specific default # Get specific default
self.default = archspec.cpu.host().name self.default = archspec.cpu.host().name

View File

@ -6,7 +6,6 @@
import archspec.cpu import archspec.cpu
import spack.target
from spack.operating_systems.linux_distro import LinuxDistro from spack.operating_systems.linux_distro import LinuxDistro
from ._platform import Platform from ._platform import Platform
@ -18,8 +17,7 @@ class Linux(Platform):
def __init__(self): def __init__(self):
super().__init__("linux") super().__init__("linux")
for name in archspec.cpu.TARGETS: self._add_archspec_targets()
self.add_target(name, spack.target.Target(name))
# Get specific default # Get specific default
self.default = archspec.cpu.host().name self.default = archspec.cpu.host().name

View File

@ -4,8 +4,9 @@
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
import platform import platform
import archspec.cpu
import spack.operating_systems import spack.operating_systems
import spack.target
from ._platform import Platform from ._platform import Platform
@ -32,8 +33,8 @@ class Test(Platform):
def __init__(self, name=None): def __init__(self, name=None):
name = name or "test" name = name or "test"
super().__init__(name) super().__init__(name)
self.add_target(self.default, spack.target.Target(self.default)) self.add_target(self.default, archspec.cpu.TARGETS[self.default])
self.add_target(self.front_end, spack.target.Target(self.front_end)) self.add_target(self.front_end, archspec.cpu.TARGETS[self.front_end])
self.add_operating_system( self.add_operating_system(
self.default_os, spack.operating_systems.OperatingSystem("debian", 6) self.default_os, spack.operating_systems.OperatingSystem("debian", 6)

View File

@ -7,7 +7,6 @@
import archspec.cpu import archspec.cpu
import spack.target
from spack.operating_systems.windows_os import WindowsOs from spack.operating_systems.windows_os import WindowsOs
from ._platform import Platform from ._platform import Platform
@ -18,9 +17,7 @@ class Windows(Platform):
def __init__(self): def __init__(self):
super().__init__("windows") super().__init__("windows")
self._add_archspec_targets()
for name in archspec.cpu.TARGETS:
self.add_target(name, spack.target.Target(name))
self.default = archspec.cpu.host().name self.default = archspec.cpu.host().name
self.front_end = self.default self.front_end = self.default

View File

@ -2479,7 +2479,7 @@ def _all_targets_satisfiying(single_constraint):
return allowed_targets return allowed_targets
cache = {} 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 # Construct the list of allowed targets for this constraint
allowed_targets = [] allowed_targets = []
for single_constraint in str(target_constraint).split(","): 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( candidate = KnownCompiler(
spec=spec.compiler, spec=spec.compiler,
os=str(spec.architecture.os), os=str(spec.architecture.os),
target=str(spec.architecture.target.microarchitecture.family), target=str(spec.architecture.target.family),
available=False, available=False,
compiler_obj=None, compiler_obj=None,
) )

View File

@ -61,6 +61,8 @@
import warnings import warnings
from typing import Any, Callable, Dict, List, Match, Optional, Set, Tuple, Union from typing import Any, Callable, Dict, List, Match, Optional, Set, Tuple, Union
import archspec.cpu
import llnl.path import llnl.path
import llnl.string import llnl.string
import llnl.util.filesystem as fs import llnl.util.filesystem as fs
@ -82,7 +84,6 @@
import spack.repo import spack.repo
import spack.solver import spack.solver
import spack.store import spack.store
import spack.target
import spack.traverse as traverse import spack.traverse as traverse
import spack.util.executable import spack.util.executable
import spack.util.hash 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 @lang.lazy_lexicographic_ordering
class ArchSpec: class ArchSpec:
"""Aggregate the target platform, the operating system and the target microarchitecture.""" """Aggregate the target platform, the operating system and the target microarchitecture."""
@ -301,7 +308,10 @@ def _autospec(self, spec_like):
def _cmp_iter(self): def _cmp_iter(self):
yield self.platform yield self.platform
yield self.os yield self.os
yield self.target if self.target is None:
yield self.target
else:
yield self.target.name
@property @property
def platform(self): def platform(self):
@ -360,10 +370,10 @@ def target(self, value):
# will assumed to be the host machine's platform. # will assumed to be the host machine's platform.
def target_or_none(t): def target_or_none(t):
if isinstance(t, spack.target.Target): if isinstance(t, archspec.cpu.Microarchitecture):
return t return t
if t and t != "None": if t and t != "None":
return spack.target.Target(t) return _make_microarchitecture(t)
return None return None
value = target_or_none(value) value = target_or_none(value)
@ -452,10 +462,11 @@ def _target_constrain(self, other: "ArchSpec") -> bool:
results = self._target_intersection(other) results = self._target_intersection(other)
attribute_str = ",".join(results) attribute_str = ",".join(results)
if self.target == attribute_str: intersection_target = _make_microarchitecture(attribute_str)
if self.target == intersection_target:
return False return False
self.target = attribute_str self.target = intersection_target
return True return True
def _target_intersection(self, other): def _target_intersection(self, other):
@ -473,7 +484,7 @@ def _target_intersection(self, other):
# s_target_range is a concrete target # s_target_range is a concrete target
# get a microarchitecture reference for at least one side # get a microarchitecture reference for at least one side
# of each comparison so we can use archspec comparators # 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 not o_sep:
if s_min == o_min: if s_min == o_min:
results.append(s_min) results.append(s_min)
@ -481,21 +492,21 @@ def _target_intersection(self, other):
results.append(s_min) results.append(s_min)
elif not o_sep: elif not o_sep:
# "cast" to microarchitecture # "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): if (not s_min or o_comp >= s_min) and (not s_max or o_comp <= s_max):
results.append(o_min) results.append(o_min)
else: else:
# Take intersection of two ranges # Take intersection of two ranges
# Lots of comparisons needed # Lots of comparisons needed
_s_min = spack.target.Target(s_min).microarchitecture _s_min = _make_microarchitecture(s_min)
_s_max = spack.target.Target(s_max).microarchitecture _s_max = _make_microarchitecture(s_max)
_o_min = spack.target.Target(o_min).microarchitecture _o_min = _make_microarchitecture(o_min)
_o_max = spack.target.Target(o_max).microarchitecture _o_max = _make_microarchitecture(o_max)
n_min = s_min if _s_min >= _o_min else o_min 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_max = s_max if _s_max <= _o_max else o_max
_n_min = spack.target.Target(n_min).microarchitecture _n_min = _make_microarchitecture(n_min)
_n_max = spack.target.Target(n_max).microarchitecture _n_max = _make_microarchitecture(n_max)
if _n_min == _n_max: if _n_min == _n_max:
results.append(n_min) results.append(n_min)
elif not n_min or not n_max or _n_min < _n_max: 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): 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( d = syaml.syaml_dict(
[ [("platform", self.platform), ("platform_os", self.os), ("target", target_data)]
("platform", self.platform),
("platform_os", self.os),
("target", self.target.to_dict_or_value()),
]
) )
return syaml.syaml_dict([("arch", d)]) return syaml.syaml_dict([("arch", d)])
@ -561,7 +578,10 @@ def to_dict(self):
def from_dict(d): def from_dict(d):
"""Import an ArchSpec from raw YAML/JSON data""" """Import an ArchSpec from raw YAML/JSON data"""
arch = d["arch"] 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)) return ArchSpec((arch["platform"], arch["platform_os"], target))
def __str__(self): def __str__(self):
@ -4120,9 +4140,7 @@ def os(self):
@property @property
def target(self): def target(self):
# This property returns the underlying microarchitecture object return self.architecture.target
# to give to the attribute the appropriate comparison semantic
return self.architecture.target.microarchitecture
@property @property
def build_spec(self): def build_spec(self):

View File

@ -33,7 +33,6 @@
import spack.caches import spack.caches
import spack.config import spack.config
import spack.error import spack.error
import spack.fetch_strategy as fs
import spack.mirror import spack.mirror
import spack.resource import spack.resource
import spack.spec import spack.spec
@ -43,6 +42,7 @@
import spack.util.path as sup import spack.util.path as sup
import spack.util.pattern as pattern import spack.util.pattern as pattern
import spack.util.url as url_util 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.crypto import bit_length, prefix_bits
from spack.util.editor import editor, executable from spack.util.editor import editor, executable
from spack.version import StandardVersion, VersionList from spack.version import StandardVersion, VersionList
@ -352,8 +352,8 @@ def __init__(
url_or_fetch_strategy, url_or_fetch_strategy,
*, *,
name=None, name=None,
mirror_paths: Optional[spack.mirror.MirrorLayout] = None, mirror_paths: Optional["spack.mirror.MirrorLayout"] = None,
mirrors: Optional[Iterable[spack.mirror.Mirror]] = None, mirrors: Optional[Iterable["spack.mirror.Mirror"]] = None,
keep=False, keep=False,
path=None, path=None,
lock=True, lock=True,
@ -464,7 +464,7 @@ def source_path(self):
"""Returns the well-known source directory path.""" """Returns the well-known source directory path."""
return os.path.join(self.path, _source_path_subdir) 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] = [] fetchers: List[fs.FetchStrategy] = []
if not mirror_only: if not mirror_only:
fetchers.append(self.default_fetcher) fetchers.append(self.default_fetcher)
@ -600,7 +600,7 @@ def cache_local(self):
spack.caches.FETCH_CACHE.store(self.fetcher, self.mirror_layout.path) spack.caches.FETCH_CACHE.store(self.fetcher, self.mirror_layout.path)
def cache_mirror( def cache_mirror(
self, mirror: spack.caches.MirrorCache, stats: spack.mirror.MirrorStats self, mirror: "spack.caches.MirrorCache", stats: "spack.mirror.MirrorStats"
) -> None: ) -> None:
"""Perform a fetch if the resource is not already cached """Perform a fetch if the resource is not already cached
@ -668,7 +668,7 @@ def destroy(self):
class ResourceStage(Stage): class ResourceStage(Stage):
def __init__( def __init__(
self, self,
fetch_strategy: fs.FetchStrategy, fetch_strategy: "fs.FetchStrategy",
root: Stage, root: Stage,
resource: spack.resource.Resource, resource: spack.resource.Resource,
**kwargs, **kwargs,

View File

@ -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
)

View File

@ -12,7 +12,6 @@
import spack.concretize import spack.concretize
import spack.operating_systems import spack.operating_systems
import spack.platforms import spack.platforms
import spack.target
from spack.spec import ArchSpec, Spec 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"} 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( @pytest.mark.parametrize(
"item,architecture_str", "item,architecture_str",
[ [
@ -118,68 +98,6 @@ def test_arch_spec_container_semantic(item, architecture_str):
assert item in architecture 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.regression("15306")
@pytest.mark.parametrize( @pytest.mark.parametrize(
"architecture_tuple,constraint_tuple", "architecture_tuple,constraint_tuple",

View File

@ -9,6 +9,8 @@
import pytest import pytest
import archspec.cpu
from llnl.path import Path, convert_to_platform_path from llnl.path import Path, convert_to_platform_path
from llnl.util.filesystem import HeaderList, LibraryList from llnl.util.filesystem import HeaderList, LibraryList
@ -737,3 +739,64 @@ def test_rpath_with_duplicate_link_deps():
assert child in rpath_deps assert child in rpath_deps
assert runtime_2 in rpath_deps assert runtime_2 in rpath_deps
assert runtime_1 not 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

View File

@ -407,7 +407,7 @@ def test_substitute_config_variables(mock_low_high_config, monkeypatch):
) == os.path.abspath(os.path.join("foo", "test", "bar")) ) == os.path.abspath(os.path.join("foo", "test", "bar"))
host_target = spack.platforms.host().target("default_target") 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( assert spack_path.canonicalize_path(
os.path.join("foo", "$target_family", "bar") os.path.join("foo", "$target_family", "bar")
) == os.path.abspath(os.path.join("foo", host_target_family, "bar")) ) == os.path.abspath(os.path.join("foo", host_target_family, "bar"))

View File

@ -71,7 +71,7 @@ def replacements():
"operating_system": lambda: arch.os, "operating_system": lambda: arch.os,
"os": lambda: arch.os, "os": lambda: arch.os,
"target": lambda: arch.target, "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"), "date": lambda: date.today().strftime("%Y-%m-%d"),
"env": lambda: ev.active_environment().path if ev.active_environment() else NOMATCH, "env": lambda: ev.active_environment().path if ev.active_environment() else NOMATCH,
} }

View File

@ -7,6 +7,7 @@
from llnl.util import tty from llnl.util import tty
from spack.build_environment import optimization_flags
from spack.package import * from spack.package import *
from spack.pkg.builtin.fftw import FftwBase from spack.pkg.builtin.fftw import FftwBase
@ -213,10 +214,7 @@ def configure(self, spec, prefix):
# variable to set AMD_ARCH configure option. # variable to set AMD_ARCH configure option.
# Spack user can not directly use AMD_ARCH for this purpose but should # Spack user can not directly use AMD_ARCH for this purpose but should
# use target variable to set appropriate -march option in AMD_ARCH. # use target variable to set appropriate -march option in AMD_ARCH.
arch = spec.architecture options.append(f"AMD_ARCH={optimization_flags(self.compiler, spec.target)}")
options.append(
"AMD_ARCH={0}".format(arch.target.optimization_flags(spec.compiler).split("=")[-1])
)
# Specific SIMD support. # Specific SIMD support.
# float and double precisions are supported # float and double precisions are supported

View File

@ -2,7 +2,7 @@
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
from spack.build_environment import optimization_flags
from spack.package import * from spack.package import *
@ -110,21 +110,23 @@ def build_targets(self):
return ["all", "html"] if "+doc" in self.spec else ["all"] return ["all", "html"] if "+doc" in self.spec else ["all"]
def cmake_args(self): def cmake_args(self):
spec = self.spec
args = [ args = [
self.define_from_variant("ARB_WITH_ASSERTIONS", "assertions"), self.define_from_variant("ARB_WITH_ASSERTIONS", "assertions"),
self.define_from_variant("ARB_WITH_MPI", "mpi"), self.define_from_variant("ARB_WITH_MPI", "mpi"),
self.define_from_variant("ARB_WITH_PYTHON", "python"), self.define_from_variant("ARB_WITH_PYTHON", "python"),
self.define_from_variant("ARB_VECTORIZE", "vectorize"), 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"): if self.spec.satisfies("+cuda"):
args.append("-DARB_GPU=cuda") args.extend(
args.append(self.define_from_variant("ARB_USE_GPU_RNG", "gpu_rng")) [
self.define("ARB_GPU", "cuda"),
# query spack for the architecture-specific compiler flags set by its wrapper self.define_from_variant("ARB_USE_GPU_RNG", "gpu_rng"),
args.append("-DARB_ARCH=none") ]
opt_flags = self.spec.architecture.target.optimization_flags(self.spec.compiler) )
args.append("-DARB_CXX_FLAGS_TARGET=" + opt_flags)
return args return args

View File

@ -7,6 +7,7 @@
import platform import platform
import re import re
from spack.build_environment import optimization_flags
from spack.package import * from spack.package import *
@ -161,7 +162,7 @@ def edit(self, spec, prefix):
if spec.satisfies("%intel"): if spec.satisfies("%intel"):
# with intel-parallel-studio+mpi the '-march' arguments # with intel-parallel-studio+mpi the '-march' arguments
# are not passed to icc # 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["@CCFLAGS@"] = f"-O3 -restrict -ansi-alias -ip {arch_opt}"
self.config["@CCNOOPT@"] = "-restrict" self.config["@CCNOOPT@"] = "-restrict"
self._write_make_arch(spec, prefix) self._write_make_arch(spec, prefix)

View File

@ -5,6 +5,7 @@
import datetime as dt import datetime as dt
import os import os
from spack.build_environment import optimization_flags
from spack.package import * from spack.package import *
@ -898,7 +899,7 @@ def cmake_args(self):
args.append(self.define("CMAKE_CXX_FLAGS_RELWITHDEBINFO", cxx_flags)) args.append(self.define("CMAKE_CXX_FLAGS_RELWITHDEBINFO", cxx_flags))
# Overwrite generic cpu tune option # 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("CMAKE_TUNE_FLAGS", cmake_tune_flags))
args.append(self.define_from_variant("LAMMPS_SIZES", "lammps_sizes")) args.append(self.define_from_variant("LAMMPS_SIZES", "lammps_sizes"))

View File

@ -9,6 +9,7 @@
import llnl.util.tty as tty import llnl.util.tty as tty
from spack.build_environment import optimization_flags
from spack.package import * from spack.package import *
@ -175,7 +176,7 @@ def _edit_arch_generic(self, spec, prefix):
# this options are take from the default provided # this options are take from the default provided
# configuration files # configuration files
# https://github.com/UIUC-PPL/charm/pull/2778 # 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"): if self.spec.satisfies("^charmpp@:6.10.1"):
optims_opts = { optims_opts = {

View File

@ -3,6 +3,7 @@
# #
# SPDX-License-Identifier: (Apache-2.0 OR MIT) # SPDX-License-Identifier: (Apache-2.0 OR MIT)
from spack.build_environment import optimization_flags
from spack.package import * 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 # add cpu arch specific optimisation flags to CMake so that they are passed
# to embedded Makefile that neuron has for compiling MOD files # 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)) args.append(self.define("CMAKE_CXX_FLAGS", compilation_flags))
return args return args

View File

@ -8,6 +8,7 @@
import sys import sys
import tempfile import tempfile
from spack.build_environment import optimization_flags
from spack.package import * from spack.package import *
rocm_dependencies = [ rocm_dependencies = [
@ -656,7 +657,7 @@ def setup_build_environment(self, env):
# Please specify optimization flags to use during compilation when # Please specify optimization flags to use during compilation when
# bazel option '--config=opt' is specified # 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 # Would you like to interactively configure ./WORKSPACE for
# Android builds? # Android builds?

View File

@ -83,7 +83,7 @@ def determine_variants(cls, libs, ver_str):
return variants return variants
def _spec_arch_to_sdk_arch(self): 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 _64bit = "64" in spec_arch
arm = "arm" in spec_arch arm = "arm" in spec_arch
if arm: if arm: