From ca6e75c9f678ecf603354531cfec68ab4a121def Mon Sep 17 00:00:00 2001 From: "Seth R. Johnson" Date: Fri, 24 Jan 2020 09:19:05 -0500 Subject: Use Spack target architecture to determine OpenBLAS target (#14380) Openblas target is now determined automatically upon inspection of `TargetList.txt`. If the spack target is a generic architecture family (like x86_64 or aarch64) the DYNAMIC_ARCH setting is used instead of targeting a specific microarchitecture. --- lib/spack/spack/test/llnl/util/cpu.py | 11 ++ .../repos/builtin/packages/openblas/package.py | 134 +++++++++++++-------- 2 files changed, 93 insertions(+), 52 deletions(-) diff --git a/lib/spack/spack/test/llnl/util/cpu.py b/lib/spack/spack/test/llnl/util/cpu.py index bcefcc04f0..300ee3c284 100644 --- a/lib/spack/spack/test/llnl/util/cpu.py +++ b/lib/spack/spack/test/llnl/util/cpu.py @@ -278,3 +278,14 @@ def test_version_components(version, expected_number, expected_suffix): number, suffix = llnl.util.cpu.version_components(version) assert number == expected_number assert suffix == expected_suffix + + +def test_invalid_family(): + targets = llnl.util.cpu.targets + multi_parents = Microarchitecture( + name='chimera', parents=[targets['pentium4'], targets['power7']], + vendor='Imagination', features=[], compilers={}, generation=0 + ) + with pytest.raises(AssertionError, + matches='a target is expected to belong'): + multi_parents.family diff --git a/var/spack/repos/builtin/packages/openblas/package.py b/var/spack/repos/builtin/packages/openblas/package.py index 09324c8414..695dcec80c 100644 --- a/var/spack/repos/builtin/packages/openblas/package.py +++ b/var/spack/repos/builtin/packages/openblas/package.py @@ -4,10 +4,10 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) import os +import re from spack import * from spack.package_test import compare_output_file, compile_c_and_execute -import spack.architecture class Openblas(MakefilePackage): @@ -33,17 +33,9 @@ class Openblas(MakefilePackage): version('0.2.16', sha256='766f350d0a4be614812d535cead8c816fc3ad3b9afcd93167ea5e4df9d61869b') version('0.2.15', sha256='73c40ace5978282224e5e122a41c8388c5a19e65a6f2329c2b7c0b61bacc9044') - variant( - 'shared', - default=True, - description='Build shared libraries as well as static libs.' - ) - variant('ilp64', default=False, description='64 bit integers') + variant('ilp64', default=False, description='Force 64-bit Fortran native integers') variant('pic', default=True, description='Build position independent code') - - variant('cpu_target', default='auto', - description='Set CPU target architecture (leave empty for ' - 'autodetection; GENERIC, SSE_GENERIC, NEHALEM, ...)') + variant('shared', default=True, description='Build shared libraries') variant( 'threads', default='none', @@ -52,24 +44,6 @@ class Openblas(MakefilePackage): multi=False ) - variant( - 'virtual_machine', - default=False, - description="Adding options to build openblas on Linux virtual machine" - ) - - variant( - 'avx2', - default=True, - description='Enable use of AVX2 instructions' - ) - - variant( - 'avx512', - default=False, - description='Enable use of AVX512 instructions' - ) - # virtual dependency provides('blas') provides('lapack') @@ -142,14 +116,80 @@ class Openblas(MakefilePackage): 'OpenBLAS @:0.2.19 does not support OpenMP with clang!' ) + @staticmethod + def _read_targets(target_file): + """Parse a list of available targets from the OpenBLAS/TargetList.txt + file. + """ + micros = [] + re_target = re.compile(r'^[A-Z0-9_]+$') + for line in target_file: + match = re_target.match(line) + if match is not None: + micros.append(line.strip().lower()) + + return micros + + @staticmethod + def _microarch_target_args(microarch, available_targets): + """Given a spack microarchitecture and a list of targets found in + OpenBLAS' TargetList.txt, determine the best command-line arguments. + """ + args = [] # Return value + + # List of available architectures, and possible aliases + openblas_arch = set(['alpha', 'arm', 'ia64', 'mips', 'mips64', + 'power', 'sparc', 'zarch']) + openblas_arch_map = { + 'amd64': 'x86_64', + 'powerpc64': 'power', + 'i386': 'x86', + 'aarch64': 'arm64', + } + openblas_arch.update(openblas_arch_map.keys()) + openblas_arch.update(openblas_arch_map.values()) + + # Add spack-only microarchitectures to list + skylake = set(["skylake", "skylake_avx512"]) + available_targets = set(available_targets) | skylake | openblas_arch + + # Find closest ancestor that is known to build in blas + if microarch.name not in available_targets: + for microarch in microarch.ancestors: + if microarch.name in available_targets: + break + + arch_name = microarch.family.name + if arch_name in openblas_arch: + # Apply possible spack->openblas arch name mapping + arch_name = openblas_arch_map.get(arch_name, arch_name) + args.append('ARCH=' + arch_name) + + if microarch.vendor == 'generic': + # User requested a generic platform, or we couldn't find a good + # match for the requested one. Allow OpenBLAS to determine + # an optimized kernel at run time. + args.append('DYNAMIC_ARCH=1') + elif microarch.name in skylake: + # Special case for renaming skylake family + args.append('TARGET=SKYLAKEX') + if microarch.name == "skylake": + # Special case for disabling avx512 instructions + args.append('NO_AVX512=1') + else: + args.append('TARGET=' + microarch.name.upper()) + + return args + @property def make_defs(self): + spec = self.spec + # Configure fails to pick up fortran from FC=/abs/path/to/fc, but # works fine with FC=/abs/path/to/gfortran. # When mixing compilers make sure that # $SPACK_ROOT/lib/spack/env/ have symlinks with reasonable # names and hack them inside lib/spack/spack/compilers/.py - make_defs = [ 'CC={0}'.format(spack_cc), 'FC={0}'.format(spack_fc), @@ -161,23 +201,15 @@ class Openblas(MakefilePackage): else: make_defs.append('MAKE_NB_JOBS=0') # flag provided by OpenBLAS - if self.spec.variants['virtual_machine'].value: - make_defs += [ - 'DYNAMIC_ARCH=1', - 'NUM_THREADS=64', # OpenBLAS stores present no of CPUs as max - ] - - if self.spec.variants['cpu_target'].value != 'auto': - make_defs += [ - 'TARGET={0}'.format(self.spec.variants['cpu_target'].value) - ] - # invoke make with the correct TARGET for aarch64 - elif 'aarch64' in spack.architecture.sys_type(): - make_defs += [ - 'TARGET=ARMV8' - ] - if self.spec.satisfies('%gcc@:4.8.4'): - make_defs += ['NO_AVX2=1'] + # Add target and architecture flags + targetlist_name = join_path(self.stage.source_path, "TargetList.txt") + if os.path.exists(targetlist_name): + with open(targetlist_name) as f: + avail_targets = self._read_targets(f) + else: + avail_targets = [] + make_defs += self._microarch_target_args(spec.target, avail_targets) + if '~shared' in self.spec: if '+pic' in self.spec: make_defs.extend([ @@ -201,11 +233,9 @@ class Openblas(MakefilePackage): if '+ilp64' in self.spec: make_defs += ['INTERFACE64=1'] - if self.spec.target.family == 'x86_64': - if '~avx2' in self.spec: - make_defs += ['NO_AVX2=1'] - if '~avx512' in self.spec: - make_defs += ['NO_AVX512=1'] + # Prevent errors in `as` assembler from newer instructions + if self.spec.satisfies('%gcc@:4.8.4'): + make_defs.append('NO_AVX2=1') return make_defs -- cgit v1.2.3-60-g2f50