microarchitectures: fix custom compiler versions (#13222)
Custom string versions for compilers were raising a ValueError on conversion to int. This commit fixes the behavior by trying to detect the underlying compiler version when in presence of a custom string version. * Refactor code that deals with custom versions for better readability * Partition version components with a regex * Fix semantic of custom compiler versions with a suffix * clang@x.y-apple has been special-cased * Add unit tests
This commit is contained in:
		
				
					committed by
					
						
						Todd Gamblin
					
				
			
			
				
	
			
			
			
						parent
						
							4ab63c17d5
						
					
				
				
					commit
					498f448ef3
				
			@@ -5,6 +5,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from .microarchitecture import Microarchitecture, UnsupportedMicroarchitecture
 | 
					from .microarchitecture import Microarchitecture, UnsupportedMicroarchitecture
 | 
				
			||||||
from .microarchitecture import targets, generic_microarchitecture
 | 
					from .microarchitecture import targets, generic_microarchitecture
 | 
				
			||||||
 | 
					from .microarchitecture import version_components
 | 
				
			||||||
from .detect import host
 | 
					from .detect import host
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = [
 | 
					__all__ = [
 | 
				
			||||||
@@ -12,5 +13,6 @@
 | 
				
			|||||||
    'UnsupportedMicroarchitecture',
 | 
					    'UnsupportedMicroarchitecture',
 | 
				
			||||||
    'targets',
 | 
					    'targets',
 | 
				
			||||||
    'generic_microarchitecture',
 | 
					    'generic_microarchitecture',
 | 
				
			||||||
    'host'
 | 
					    'host',
 | 
				
			||||||
 | 
					    'version_components'
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,6 +4,7 @@
 | 
				
			|||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
					# SPDX-License-Identifier: (Apache-2.0 OR MIT)
 | 
				
			||||||
import functools
 | 
					import functools
 | 
				
			||||||
import platform
 | 
					import platform
 | 
				
			||||||
 | 
					import re
 | 
				
			||||||
import warnings
 | 
					import warnings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
@@ -219,9 +220,9 @@ def satisfies_constraint(entry, version):
 | 
				
			|||||||
            min_version, max_version = entry['versions'].split(':')
 | 
					            min_version, max_version = entry['versions'].split(':')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Check version suffixes
 | 
					            # Check version suffixes
 | 
				
			||||||
            min_version, _, min_suffix = min_version.partition('-')
 | 
					            min_version, min_suffix = version_components(min_version)
 | 
				
			||||||
            max_version, _, max_suffix = max_version.partition('-')
 | 
					            max_version, max_suffix = version_components(max_version)
 | 
				
			||||||
            version, _, suffix = version.partition('-')
 | 
					            version, suffix = version_components(version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # If the suffixes are not all equal there's no match
 | 
					            # If the suffixes are not all equal there's no match
 | 
				
			||||||
            if ((suffix != min_suffix and min_version) or
 | 
					            if ((suffix != min_suffix and min_version) or
 | 
				
			||||||
@@ -277,6 +278,26 @@ def generic_microarchitecture(name):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def version_components(version):
 | 
				
			||||||
 | 
					    """Decomposes the version passed as input in version number and
 | 
				
			||||||
 | 
					    suffix and returns them.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    If the version number of the suffix are not present, an empty
 | 
				
			||||||
 | 
					    string is returned.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Args:
 | 
				
			||||||
 | 
					        version (str): version to be decomposed into its components
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    match = re.match(r'([\d.]*)(-?)(.*)', str(version))
 | 
				
			||||||
 | 
					    if not match:
 | 
				
			||||||
 | 
					        return '', ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    version_number = match.group(1)
 | 
				
			||||||
 | 
					    suffix = match.group(3)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return version_number, suffix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _known_microarchitectures():
 | 
					def _known_microarchitectures():
 | 
				
			||||||
    """Returns a dictionary of the known micro-architectures. If the
 | 
					    """Returns a dictionary of the known micro-architectures. If the
 | 
				
			||||||
    current host platform is unknown adds it too as a generic target.
 | 
					    current host platform is unknown adds it too as a generic target.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -192,6 +192,8 @@ def optimization_flags(self, compiler):
 | 
				
			|||||||
            compiler (CompilerSpec or Compiler): object that contains both the
 | 
					            compiler (CompilerSpec or Compiler): object that contains both the
 | 
				
			||||||
                name and the version of the compiler we want to use
 | 
					                name and the version of the compiler we want to use
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					        # Mixed toolchains are not supported yet
 | 
				
			||||||
 | 
					        import spack.compilers
 | 
				
			||||||
        if isinstance(compiler, spack.compiler.Compiler):
 | 
					        if isinstance(compiler, spack.compiler.Compiler):
 | 
				
			||||||
            if spack.compilers.is_mixed_toolchain(compiler):
 | 
					            if spack.compilers.is_mixed_toolchain(compiler):
 | 
				
			||||||
                msg = ('microarchitecture specific optimizations are not '
 | 
					                msg = ('microarchitecture specific optimizations are not '
 | 
				
			||||||
@@ -200,8 +202,22 @@ def optimization_flags(self, compiler):
 | 
				
			|||||||
                warnings.warn(msg.format(compiler))
 | 
					                warnings.warn(msg.format(compiler))
 | 
				
			||||||
                return ''
 | 
					                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 = cpu.version_components(compiler.version)
 | 
				
			||||||
 | 
					        if not version_number or suffix not in ('', 'apple'):
 | 
				
			||||||
 | 
					            # Try to deduce the correct version. Depending on where this
 | 
				
			||||||
 | 
					            # function is called we might get either a CompilerSpec or a
 | 
				
			||||||
 | 
					            # fully fledged compiler object
 | 
				
			||||||
 | 
					            import spack.spec
 | 
				
			||||||
 | 
					            if isinstance(compiler, spack.spec.CompilerSpec):
 | 
				
			||||||
 | 
					                compiler = spack.compilers.compilers_for_spec(compiler).pop()
 | 
				
			||||||
 | 
					            compiler_version = compiler.cc_version(compiler.cc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return self.microarchitecture.optimization_flags(
 | 
					        return self.microarchitecture.optimization_flags(
 | 
				
			||||||
            compiler.name, str(compiler.version)
 | 
					            compiler.name, str(compiler_version)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -187,3 +187,29 @@ def test_optimization_flags(
 | 
				
			|||||||
    compiler = spack.compilers.compilers_for_spec(compiler_spec).pop()
 | 
					    compiler = spack.compilers.compilers_for_spec(compiler_spec).pop()
 | 
				
			||||||
    opt_flags = target.optimization_flags(compiler)
 | 
					    opt_flags = target.optimization_flags(compiler)
 | 
				
			||||||
    assert opt_flags == expected_flags
 | 
					    assert opt_flags == expected_flags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.mark.parametrize('compiler,real_version,target_str,expected_flags', [
 | 
				
			||||||
 | 
					    (spack.spec.CompilerSpec('gcc@9.2.0'), None, 'haswell',
 | 
				
			||||||
 | 
					     '-march=haswell -mtune=haswell'),
 | 
				
			||||||
 | 
					    # Check that custom string versions are accepted
 | 
				
			||||||
 | 
					    (spack.spec.CompilerSpec('gcc@foo'), '9.2.0', 'icelake',
 | 
				
			||||||
 | 
					     '-march=icelake-client -mtune=icelake-client'),
 | 
				
			||||||
 | 
					    # Check that we run version detection (4.4.0 doesn't support icelake)
 | 
				
			||||||
 | 
					    (spack.spec.CompilerSpec('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 dtect the version again
 | 
				
			||||||
 | 
					    (spack.spec.CompilerSpec('clang@9.1.0-apple'), None, 'x86_64',
 | 
				
			||||||
 | 
					     '-march=x86-64 -mcpu=generic'),
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					def test_optimization_flags_with_custom_versions(
 | 
				
			||||||
 | 
					        compiler, real_version, target_str, expected_flags, monkeypatch, config
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    target = spack.architecture.Target(target_str)
 | 
				
			||||||
 | 
					    if real_version:
 | 
				
			||||||
 | 
					        monkeypatch.setattr(
 | 
				
			||||||
 | 
					            spack.compiler.Compiler, 'cc_version', lambda x, y: real_version
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    opt_flags = target.optimization_flags(compiler)
 | 
				
			||||||
 | 
					    assert opt_flags == expected_flags
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,3 +135,21 @@ compilers:
 | 
				
			|||||||
      f77: None
 | 
					      f77: None
 | 
				
			||||||
      fc: None
 | 
					      fc: None
 | 
				
			||||||
    modules: 'None'
 | 
					    modules: 'None'
 | 
				
			||||||
 | 
					- compiler:
 | 
				
			||||||
 | 
					    spec: gcc@foo
 | 
				
			||||||
 | 
					    operating_system: redhat6
 | 
				
			||||||
 | 
					    paths:
 | 
				
			||||||
 | 
					      cc: /path/to/gcc
 | 
				
			||||||
 | 
					      cxx: /path/to/g++
 | 
				
			||||||
 | 
					      f77: /path/to/gfortran
 | 
				
			||||||
 | 
					      fc: /path/to/gfortran
 | 
				
			||||||
 | 
					    modules: 'None'
 | 
				
			||||||
 | 
					- compiler:
 | 
				
			||||||
 | 
					    spec: gcc@4.4.0-special
 | 
				
			||||||
 | 
					    operating_system: redhat6
 | 
				
			||||||
 | 
					    paths:
 | 
				
			||||||
 | 
					      cc: /path/to/gcc
 | 
				
			||||||
 | 
					      cxx: /path/to/g++
 | 
				
			||||||
 | 
					      f77: /path/to/gfortran
 | 
				
			||||||
 | 
					      fc: /path/to/gfortran
 | 
				
			||||||
 | 
					    modules: 'None'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -242,3 +242,15 @@ def test_automatic_conversion_on_comparisons(operation, expected_result):
 | 
				
			|||||||
    target = llnl.util.cpu.targets[target]
 | 
					    target = llnl.util.cpu.targets[target]
 | 
				
			||||||
    code = 'target ' + operator + 'other_target'
 | 
					    code = 'target ' + operator + 'other_target'
 | 
				
			||||||
    assert eval(code) is expected_result
 | 
					    assert eval(code) is expected_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.mark.parametrize('version,expected_number,expected_suffix', [
 | 
				
			||||||
 | 
					    ('4.2.0', '4.2.0', ''),
 | 
				
			||||||
 | 
					    ('4.2.0-apple', '4.2.0', 'apple'),
 | 
				
			||||||
 | 
					    ('my-funny-name-with-dashes', '', 'my-funny-name-with-dashes'),
 | 
				
			||||||
 | 
					    ('10.3.56~svnr64537', '10.3.56', '~svnr64537')
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user