Skip to content

Commit c5019c1

Browse files
authored
Merge pull request #4860 from Thyre/support-passing-amdgcn
Add AMDGCN option similar to `cuda-compute-capabilities`
2 parents f4e855b + f9dec88 commit c5019c1

File tree

6 files changed

+140
-1
lines changed

6 files changed

+140
-1
lines changed

easybuild/framework/easyconfig/default.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
'toolchainopts': [None, 'Extra options for compilers', TOOLCHAIN],
8585

8686
# BUILD easyconfig parameters
87+
'amdgcn_capabilities': [[], "List of AMDGCN capabilities to build with (if supported)", BUILD],
8788
'banned_linked_shared_libs': [[], "List of shared libraries (names, file names, or paths) which are not allowed "
8889
"to be linked in any installed binary/library", BUILD],
8990
'bitbucket_account': ['%(namelower)s', "Bitbucket account name to be used to resolve template values in source"

easybuild/framework/easyconfig/easyconfig.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,28 @@ def get_cuda_cc_template_value(self, key, required=True):
19921992
error_msg = "%s is not a template value based on --cuda-compute-capabilities/cuda_compute_capabilities"
19931993
raise EasyBuildError(error_msg, key)
19941994

1995+
def get_amdgcn_cc_template_value(self, key, required=True):
1996+
"""
1997+
Get template value based on --amdgcn-capabilities EasyBuild configuration option
1998+
and amdgcn_capabilities easyconfig parameter.
1999+
Returns user-friendly error message in case neither are defined,
2000+
or if an unknown key is used.
2001+
"""
2002+
if key.startswith('amdgcn_') and any(x == key for x in TEMPLATE_NAMES_DYNAMIC):
2003+
try:
2004+
return self.template_values[key]
2005+
except KeyError:
2006+
if not required:
2007+
self.log.debug(f'Key {key} not found in template values, returning empty value')
2008+
return ''
2009+
error_msg = "Template value '%s' is not defined!\n"
2010+
error_msg += "Make sure that either the --amdgcn-capabilities EasyBuild configuration "
2011+
error_msg += "option is set, or that the amdgcn_capabilities easyconfig parameter is defined."
2012+
raise EasyBuildError(error_msg, key)
2013+
else:
2014+
error_msg = "%s is not a template value based on --amdgcn-capabilities/amdgcn_capabilities"
2015+
raise EasyBuildError(error_msg, key)
2016+
19952017

19962018
def det_installversion(version, toolchain_name, toolchain_version, prefix, suffix):
19972019
"""Deprecated 'det_installversion' function, to determine exact install version, based on supplied parameters."""

easybuild/framework/easyconfig/templates.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@
8888
# template values which are only generated dynamically
8989
TEMPLATE_NAMES_DYNAMIC = {
9090
'arch': 'System architecture (e.g. x86_64, aarch64, ppc64le, ...)',
91+
'amdgcn_capabilities': "Comma-separated list of AMDGCN capabilities, as specified via "
92+
"--amdgcn-capabilities configuration option or "
93+
"via amdgcn_capabilities easyconfig parameter",
94+
'amdgcn_cc_space_sep': "Space-separated list of AMDGCN capabilities",
95+
'amdgcn_cc_semicolon_sep': "Semicolon-separated list of AMDGCN capabilities",
9196
'cuda_compute_capabilities': "Comma-separated list of CUDA compute capabilities, as specified via "
9297
"--cuda-compute-capabilities configuration option or "
9398
"via cuda_compute_capabilities easyconfig parameter",
@@ -478,6 +483,14 @@ def template_constant_dict(config, ignore=None, toolchain=None):
478483
template_values['cuda_sm_comma_sep'] = ','.join(sm_values)
479484
template_values['cuda_sm_space_sep'] = ' '.join(sm_values)
480485

486+
# step 7. AMDGCN capabilities
487+
# Use the commandline / easybuild config option if given, else use the value from the EC (as a default)
488+
amdgcn_cc = build_option('amdgcn_capabilities') or config.get('amdgcn_capabilities')
489+
if amdgcn_cc:
490+
template_values['amdgcn_capabilities'] = ','.join(amdgcn_cc)
491+
template_values['amdgcn_cc_space_sep'] = ' '.join(amdgcn_cc)
492+
template_values['amdgcn_cc_semicolon_sep'] = ';'.join(amdgcn_cc)
493+
481494
unknown_names = []
482495
for key in template_values:
483496
if not (key in common_template_names or key in TEMPLATE_NAMES_DYNAMIC):

easybuild/tools/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
228228
BUILD_OPTIONS_CMDLINE = {
229229
None: [
230230
'aggregate_regtest',
231+
'amdgcn_capabilities',
231232
'backup_modules',
232233
'banned_linked_shared_libs',
233234
'checksum_priority',

easybuild/tools/options.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ def override_options(self):
370370
None, 'store_true', False),
371371
'allow-use-as-root-and-accept-consequences': ("Allow using of EasyBuild as root (NOT RECOMMENDED!)",
372372
None, 'store_true', False),
373+
'amdgcn-capabilities': ("List of AMDGCN capabilities to use when building GPU software; "
374+
"values should be specified as gfx[xyz], as defined by the LLVM targets, "
375+
"for example: gfx1101,gfx90a,gfx1030", 'strlist', 'extend', None),
373376
'backup-modules': ("Back up an existing module file, if any. "
374377
"Auto-enabled when using --module-only or --skip",
375378
None, 'store_true', None), # default None to allow auto-enabling if not disabled
@@ -993,6 +996,26 @@ def validate(self):
993996
error_msg = "Incorrect values in --cuda-compute-capabilities (expected pattern: '%s'): %s"
994997
error_msgs.append(error_msg % (cuda_cc_regex.pattern, ', '.join(faulty_cuda_ccs)))
995998

999+
# Support accelerators using the gfx[...] naming scheme.
1000+
# This applies to all AMD GPUs since Southern Islands (2013)
1001+
# For more information: https://llvm.org/docs/AMDGPUUsage.html#processors
1002+
# Allow users to pass --amdgcn-capabilities=, which will be mapped to not passing any
1003+
if self.options.amdgcn_capabilities == ['']:
1004+
self.options.amdgcn_capabilities = None
1005+
if self.options.amdgcn_capabilities:
1006+
# General accelerator naming convention
1007+
amdgcn_cc_regex = re.compile(r'gfx[0-9]+[a-z]?$')
1008+
# Generic convention.
1009+
# Regex is not perfect, as it doesn't catch gfx[...]--generic
1010+
amdgcn_generic_regex = re.compile(r'gfx[0-9]+(\-[0-9])?-generic$')
1011+
faulty_amdgcn_ccs = [x for x in self.options.amdgcn_capabilities
1012+
if not amdgcn_cc_regex.match(x) and not amdgcn_generic_regex.match(x)]
1013+
if faulty_amdgcn_ccs:
1014+
error_msg = "Incorrect values in --amdgcn-capabilities (expected pattern: '%s' or '%s'): %s"
1015+
error_msgs.append(error_msg % (amdgcn_cc_regex.pattern,
1016+
amdgcn_generic_regex.pattern,
1017+
', '.join(faulty_amdgcn_ccs)))
1018+
9961019
if error_msgs:
9971020
raise EasyBuildError(
9981021
"Found problems validating the options: %s", '\n'.join(error_msgs),
@@ -2107,7 +2130,7 @@ def set_tmpdir(tmpdir=None, raise_error=False):
21072130

21082131
# avoid having special characters like '[' and ']' in the tmpdir pathname,
21092132
# it is known to cause problems (e.g., with Python install tools, CUDA's nvcc, etc.);
2110-
# only common characteris like alphanumeric, '_', '-', '.' and '/' are retained; others are converted to 'X'
2133+
# only common characters like alphanumeric, '_', '-', '.' and '/' are retained; others are converted to 'X'
21112134
special_chars_regex = r'[^\w/.-]'
21122135
if re.search(special_chars_regex, current_tmpdir):
21132136
current_tmpdir = re.sub(special_chars_regex, 'X', current_tmpdir)

test/framework/easyconfig.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4807,6 +4807,35 @@ def test_cuda_compute_capabilities(self):
48074807
self.assertEqual(ec['preinstallopts'], 'period="4.2 6.3" noperiod="42 63"')
48084808
self.assertEqual(ec['installopts'], '4.2,6.3')
48094809

4810+
def test_amdgcn_capabilities(self):
4811+
self.contents = textwrap.dedent("""
4812+
easyblock = 'ConfigureMake'
4813+
name = 'test'
4814+
version = '0.2'
4815+
homepage = 'https://example.com'
4816+
description = 'test'
4817+
toolchain = SYSTEM
4818+
amdgcn_capabilities = ['gfx90a', 'gfx1101', 'gfx11-generic', 'gfx10-3-generic']
4819+
buildopts = ('comma="%(amdgcn_capabilities)s" space="%(amdgcn_cc_space_sep)s" '
4820+
'semi="%(amdgcn_cc_semicolon_sep)s"')
4821+
installopts = '%(amdgcn_capabilities)s'
4822+
""")
4823+
self.prep()
4824+
4825+
ec = EasyConfig(self.eb_file)
4826+
self.assertEqual(ec['buildopts'], 'comma="gfx90a,gfx1101,gfx11-generic,gfx10-3-generic" '
4827+
'space="gfx90a gfx1101 gfx11-generic gfx10-3-generic" '
4828+
'semi="gfx90a;gfx1101;gfx11-generic;gfx10-3-generic"')
4829+
self.assertEqual(ec['installopts'], 'gfx90a,gfx1101,gfx11-generic,gfx10-3-generic')
4830+
4831+
# build options overwrite it
4832+
init_config(build_options={'amdgcn_capabilities': ['gfx90a', 'gfx1101']})
4833+
ec = EasyConfig(self.eb_file)
4834+
self.assertEqual(ec['buildopts'], 'comma="gfx90a,gfx1101" '
4835+
'space="gfx90a gfx1101" '
4836+
'semi="gfx90a;gfx1101"')
4837+
self.assertEqual(ec['installopts'], 'gfx90a,gfx1101')
4838+
48104839
def test_det_copy_ec_specs(self):
48114840
"""Test det_copy_ec_specs function."""
48124841

@@ -5111,6 +5140,56 @@ def test_get_cuda_cc_template_value(self):
51115140
for key, expected in cuda_template_values.items():
51125141
self.assertEqual(ec.get_cuda_cc_template_value(key), expected)
51135142

5143+
def test_get_amdgcn_cc_template_value(self):
5144+
"""
5145+
Test getting template value based on --amdgcn-capabilities / amdgcn_capabilities.
5146+
"""
5147+
self.contents = '\n'.join([
5148+
'easyblock = "ConfigureMake"',
5149+
'name = "pi"',
5150+
'version = "3.14"',
5151+
'homepage = "http://example.com"',
5152+
'description = "test easyconfig"',
5153+
'toolchain = SYSTEM',
5154+
])
5155+
self.prep()
5156+
ec = EasyConfig(self.eb_file)
5157+
5158+
error_pattern = ("foobar is not a template value based on "
5159+
"--amdgcn-capabilities/amdgcn_capabilities")
5160+
self.assertErrorRegex(EasyBuildError, error_pattern, ec.get_amdgcn_cc_template_value, 'foobar')
5161+
5162+
error_pattern = r"Template value '%s' is not defined!\n"
5163+
error_pattern += r"Make sure that either the --amdgcn-capabilities EasyBuild configuration "
5164+
error_pattern += "option is set, or that the amdgcn_capabilities easyconfig parameter is defined."
5165+
amdgcn_template_values = {
5166+
'amdgcn_capabilities': 'gfx90a,gfx1100,gfx10-3-generic',
5167+
'amdgcn_cc_space_sep': 'gfx90a gfx1100 gfx10-3-generic',
5168+
'amdgcn_cc_semicolon_sep': 'gfx90a;gfx1100;gfx10-3-generic',
5169+
}
5170+
for key in amdgcn_template_values:
5171+
self.assertErrorRegex(EasyBuildError, error_pattern % key, ec.get_amdgcn_cc_template_value, key)
5172+
5173+
update_build_option('amdgcn_capabilities', ['gfx90a', 'gfx1100', 'gfx10-3-generic'])
5174+
ec = EasyConfig(self.eb_file)
5175+
5176+
for key, expected in amdgcn_template_values.items():
5177+
self.assertEqual(ec.get_amdgcn_cc_template_value(key), expected)
5178+
5179+
update_build_option('amdgcn_capabilities', None)
5180+
ec = EasyConfig(self.eb_file)
5181+
5182+
for key in amdgcn_template_values:
5183+
self.assertErrorRegex(EasyBuildError, error_pattern % key, ec.get_amdgcn_cc_template_value, key)
5184+
self.assertEqual(ec.get_amdgcn_cc_template_value(key, required=False), '')
5185+
5186+
self.contents += "\namdgcn_capabilities = ['gfx90a', 'gfx1100', 'gfx10-3-generic']"
5187+
self.prep()
5188+
ec = EasyConfig(self.eb_file)
5189+
5190+
for key, expected in amdgcn_template_values.items():
5191+
self.assertEqual(ec.get_amdgcn_cc_template_value(key), expected)
5192+
51145193
def test_count_files(self):
51155194
"""Tests for EasyConfig.count_files method."""
51165195
test_ecs_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'easyconfigs', 'test_ecs')

0 commit comments

Comments
 (0)