Microarchitecture specific optimizations for LLVM (#13250)
* Added architecture specific optimization flags for Clang / LLVM
* Disallow compiler optimizations for mixed toolchains
    * We emit a warning when building for a mixed toolchain
* Fixed issues with suffixed versions of compilers; Apple's Clang will, 
    for the time being, fall back on x86-64 for every compilation.
			
			
This commit is contained in:
		 Massimiliano Culpo
					Massimiliano Culpo
				
			
				
					committed by
					
						 Todd Gamblin
						Todd Gamblin
					
				
			
			
				
	
			
			
			 Todd Gamblin
						Todd Gamblin
					
				
			
						parent
						
							0fb563f3d9
						
					
				
				
					commit
					41fb0395a6
				
			| @@ -224,7 +224,8 @@ def satisfies_constraint(entry, version): | ||||
|             version, _, suffix = version.partition('-') | ||||
|  | ||||
|             # If the suffixes are not all equal there's no match | ||||
|             if suffix != min_suffix or suffix != max_suffix: | ||||
|             if ((suffix != min_suffix and min_version) or | ||||
|                 (suffix != max_suffix and max_version)): | ||||
|                 return False | ||||
|  | ||||
|             # Assume compiler versions fit into semver | ||||
|   | ||||
| @@ -60,7 +60,12 @@ | ||||
|             "name": "x86-64", | ||||
|             "flags": "-march={name} -mtune={name}" | ||||
|           } | ||||
|         ] | ||||
|         ], | ||||
|         "clang": { | ||||
|           "versions": ":", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu=generic" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "nocona": { | ||||
| @@ -76,6 +81,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "4:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -92,6 +102,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "4:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -118,7 +133,12 @@ | ||||
|             "name": "corei7", | ||||
|             "flags": "-march={name} -mtune={name}" | ||||
|           } | ||||
|         ] | ||||
|         ], | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "westmere": { | ||||
| @@ -139,6 +159,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "4.9:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -168,7 +193,12 @@ | ||||
|             "name": "corei7-avx", | ||||
|             "flags": "-march={name} -mtune={name}" | ||||
|           } | ||||
|         ] | ||||
|         ], | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "ivybridge": { | ||||
| @@ -199,7 +229,12 @@ | ||||
|             "name": "core-avx-i", | ||||
|             "flags": "-march={name} -mtune={name}" | ||||
|           } | ||||
|         ] | ||||
|         ], | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "haswell": { | ||||
| @@ -235,7 +270,12 @@ | ||||
|             "name": "core-avx2", | ||||
|             "flags": "-march={name} -mtune={name}" | ||||
|           } | ||||
|         ] | ||||
|         ], | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "broadwell": { | ||||
| @@ -266,6 +306,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "4.9:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -300,6 +345,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "5.3:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -337,6 +387,12 @@ | ||||
|           "versions": "5.1:", | ||||
|           "name": "knl", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "name": "knl", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -378,6 +434,12 @@ | ||||
|           "name": "skylake-avx512", | ||||
|           "versions": "5.3:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "name": "skylake-avx512", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -421,6 +483,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "8:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -462,6 +529,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "9:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "8.0:", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -518,7 +590,20 @@ | ||||
|           "name": "icelake-client", | ||||
|           "versions": "8:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         } | ||||
|         }, | ||||
|         "clang": [ | ||||
|           { | ||||
|             "versions": "7.0:", | ||||
|             "name": "icelake-client", | ||||
|             "family": "x86-64", | ||||
|             "flags": "-march={family} -mcpu={name}" | ||||
|           }, | ||||
|           { | ||||
|             "versions": "6.0:6.9", | ||||
|             "family": "x86-64", | ||||
|             "flags": "-march={family} -mcpu={name}" | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     "bulldozer": { | ||||
| @@ -545,6 +630,12 @@ | ||||
|           "name": "bdver1", | ||||
|           "versions": "4.6:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "name": "bdver1", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -576,6 +667,12 @@ | ||||
|           "name": "bdver2", | ||||
|           "versions": "4.7:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "name": "bdver2", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -608,6 +705,12 @@ | ||||
|           "name": "bdver3", | ||||
|           "versions": "4.8:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "name": "bdver3", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -643,6 +746,12 @@ | ||||
|           "name": "bdver4", | ||||
|           "versions": "4.9:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "name": "bdver4", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -681,6 +790,12 @@ | ||||
|           "name": "znver1", | ||||
|           "versions": "6:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "4.0:", | ||||
|           "name": "znver1", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -720,6 +835,12 @@ | ||||
|           "name": "znver2", | ||||
|           "versions": "9:", | ||||
|           "flags": "-march={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "9.0:", | ||||
|           "name": "znver2", | ||||
|           "family": "x86-64", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -732,6 +853,11 @@ | ||||
|           "name": "powerpc64", | ||||
|           "versions": "4:", | ||||
|           "flags": "-mcpu={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": ":", | ||||
|           "family": "ppc64", | ||||
|           "flags": "-march={family} -mcpu=generic" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -744,6 +870,12 @@ | ||||
|         "gcc": { | ||||
|           "versions": "4.5:", | ||||
|           "flags": "-mcpu={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "ppc64", | ||||
|           "name": "pwr7", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -763,7 +895,13 @@ | ||||
|             "warnings": "Using GCC 4.8 to optimize for Power 8 might not work if you are not on Red Hat Enterprise Linux 7, where a custom backport of the feature has been done. Upstream support from GCC starts in version 4.9", | ||||
|             "flags": "-mcpu={name} -mtune={name}" | ||||
|           } | ||||
|         ] | ||||
|         ], | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "ppc64", | ||||
|           "name": "pwr8", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "power9": { | ||||
| @@ -775,6 +913,12 @@ | ||||
|         "gcc": { | ||||
|           "versions": "6:", | ||||
|           "flags": "-mcpu={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "ppc64", | ||||
|           "name": "pwr9", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -787,6 +931,11 @@ | ||||
|           "name": "powerpc64le", | ||||
|           "versions": "4:", | ||||
|           "flags": "-mcpu={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": ":", | ||||
|           "family": "ppc64le", | ||||
|           "flags": "-march={family} -mcpu=generic" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -808,7 +957,13 @@ | ||||
|             "name": "power8", | ||||
|             "flags": "-mcpu={name} -mtune={name}" | ||||
|           } | ||||
|         ] | ||||
|         ], | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "ppc64le", | ||||
|           "name": "pwr8", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "power9le": { | ||||
| @@ -821,6 +976,12 @@ | ||||
|           "name": "power9", | ||||
|           "versions": "6:", | ||||
|           "flags": "-mcpu={name} -mtune={name}" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": "3.9:", | ||||
|           "family": "ppc64le", | ||||
|           "name": "pwr9", | ||||
|           "flags": "-march={family} -mcpu={name}" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -832,6 +993,11 @@ | ||||
|         "gcc": { | ||||
|           "versions": "4:", | ||||
|           "flags": "-march=armv8-a -mtune=generic" | ||||
|         }, | ||||
|         "clang": { | ||||
|           "versions": ":", | ||||
|           "family": "aarch64", | ||||
|           "flags": "-march={family} -mcpu=generic" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -840,6 +1006,11 @@ | ||||
|       "vendor": "generic", | ||||
|       "features": [], | ||||
|       "compilers": { | ||||
|         "clang": { | ||||
|           "versions": ":", | ||||
|           "family": "arm", | ||||
|           "flags": "-march={family} -mcpu=generic" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "ppc": { | ||||
|   | ||||
| @@ -58,6 +58,7 @@ | ||||
| """ | ||||
| import functools | ||||
| import inspect | ||||
| import warnings | ||||
|  | ||||
| import six | ||||
|  | ||||
| @@ -184,6 +185,21 @@ 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 (CompilerSpec or Compiler): object that contains both the | ||||
|                 name and the version of the compiler we want to use | ||||
|         """ | ||||
|         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]') | ||||
|                 warnings.warn(msg.format(compiler)) | ||||
|                 return '' | ||||
|  | ||||
|         return self.microarchitecture.optimization_flags( | ||||
|             compiler.name, str(compiler.version) | ||||
|         ) | ||||
|   | ||||
| @@ -676,6 +676,44 @@ def _default(cmp_id, paths): | ||||
|     return compilers | ||||
|  | ||||
|  | ||||
| def is_mixed_toolchain(compiler): | ||||
|     """Returns True if the current compiler is a mixed toolchain, | ||||
|     False otherwise. | ||||
|  | ||||
|     Args: | ||||
|         compiler (Compiler): a valid compiler object | ||||
|     """ | ||||
|     cc = os.path.basename(compiler.cc or '') | ||||
|     cxx = os.path.basename(compiler.cxx or '') | ||||
|     f77 = os.path.basename(compiler.f77 or '') | ||||
|     fc = os.path.basename(compiler.fc or '') | ||||
|  | ||||
|     toolchains = set() | ||||
|     for compiler_cls in all_compiler_types(): | ||||
|         # Inspect all the compiler toolchain we know. If a compiler is the | ||||
|         # only compiler supported there it belongs to that toolchain. | ||||
|         def name_matches(name, name_list): | ||||
|             # This is such that 'gcc' matches variations | ||||
|             # like 'ggc-9' etc that are found in distros | ||||
|             name, _, _ = name.partition('-') | ||||
|             return len(name_list) == 1 and name and name in name_list | ||||
|  | ||||
|         if any([ | ||||
|             name_matches(cc, compiler_cls.cc_names), | ||||
|             name_matches(cxx, compiler_cls.cxx_names), | ||||
|             name_matches(f77, compiler_cls.f77_names), | ||||
|             name_matches(fc, compiler_cls.fc_names) | ||||
|         ]): | ||||
|             tty.debug("[TOOLCHAIN] MATCH {0}".format(compiler_cls.__name__)) | ||||
|             toolchains.add(compiler_cls.__name__) | ||||
|  | ||||
|     if len(toolchains) > 1: | ||||
|         tty.debug("[TOOLCHAINS] {0}".format(toolchains)) | ||||
|         return True | ||||
|  | ||||
|     return False | ||||
|  | ||||
|  | ||||
| class InvalidCompilerConfigurationError(spack.error.SpackError): | ||||
|  | ||||
|     def __init__(self, compiler_spec): | ||||
|   | ||||
| @@ -169,3 +169,21 @@ def test_target_container_semantic(cpu_flag, target_name): | ||||
| def test_arch_spec_container_semantic(item, architecture_str): | ||||
|     architecture = spack.spec.ArchSpec(architecture_str) | ||||
|     assert item in architecture | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize('compiler_spec,target_name,expected_flags', [ | ||||
|     # Check compilers with version numbers from a single toolchain | ||||
|     ('gcc@4.7.2', 'haswell', '-march=core-avx2 -mtune=core-avx2'), | ||||
|     # Check mixed toolchains | ||||
|     ('clang@8.0.0', 'broadwell', ''), | ||||
|     # Check clang compilers with 'apple' suffix | ||||
|     ('clang@9.1.0-apple', 'x86_64', '-march=x86-64 -mcpu=generic') | ||||
| ]) | ||||
| @pytest.mark.filterwarnings("ignore:microarchitecture specific") | ||||
| def test_optimization_flags( | ||||
|         compiler_spec, target_name, expected_flags, config | ||||
| ): | ||||
|     target = spack.architecture.Target(target_name) | ||||
|     compiler = spack.compilers.compilers_for_spec(compiler_spec).pop() | ||||
|     opt_flags = target.optimization_flags(compiler) | ||||
|     assert opt_flags == expected_flags | ||||
|   | ||||
| @@ -469,3 +469,11 @@ def test_cce_version_detection(version_str, expected_version): | ||||
| def test_fj_version_detection(version_str, expected_version): | ||||
|     version = spack.compilers.fj.Fj.extract_version_from_output(version_str) | ||||
|     assert version == expected_version | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize('compiler_spec,expected_result', [ | ||||
|     ('gcc@4.7.2', False), ('clang@3.3', False), ('clang@8.0.0', True) | ||||
| ]) | ||||
| def test_detecting_mixed_toolchains(compiler_spec, expected_result, config): | ||||
|     compiler = spack.compilers.compilers_for_spec(compiler_spec).pop() | ||||
|     assert spack.compilers.is_mixed_toolchain(compiler) is expected_result | ||||
|   | ||||
| @@ -585,7 +585,9 @@ def test_noversion_pkg(self, spec): | ||||
|  | ||||
|     @pytest.mark.parametrize('spec, best_achievable', [ | ||||
|         ('mpileaks%gcc@4.8', 'haswell'), | ||||
|         ('mpileaks%gcc@5.3.0', 'skylake_avx512') | ||||
|         ('mpileaks%gcc@5.3.0', 'skylake_avx512'), | ||||
|         # Apple's clang always falls back to x86-64 for now | ||||
|         ('mpileaks%clang@9.1.0-apple', 'x86_64') | ||||
|     ]) | ||||
|     def test_adjusting_default_target_based_on_compiler( | ||||
|             self, spec, best_achievable, current_host | ||||
|   | ||||
| @@ -114,3 +114,24 @@ compilers: | ||||
|       cflags: -O3 | ||||
|       cxxflags: -O3 | ||||
|     modules: 'None' | ||||
| - compiler: | ||||
|     spec: clang@8.0.0 | ||||
|     operating_system: redhat7 | ||||
|     paths: | ||||
|       cc: /path/to/clang-8 | ||||
|       cxx: /path/to/clang++-8 | ||||
|       f77: /path/to/gfortran-9 | ||||
|       fc: /path/to/gfortran-9 | ||||
|     flags: | ||||
|       cflags: -O3 | ||||
|       cxxflags: -O3 | ||||
|     modules: 'None' | ||||
| - compiler: | ||||
|     spec: clang@9.1.0-apple | ||||
|     operating_system: elcapitan | ||||
|     paths: | ||||
|       cc: /path/to/clang | ||||
|       cxx: /path/to/clang++ | ||||
|       f77: None | ||||
|       fc: None | ||||
|     modules: 'None' | ||||
|   | ||||
| @@ -191,6 +191,7 @@ def test_target_json_schema(): | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize('target_name,compiler,version,expected_flags', [ | ||||
|     # Test GCC | ||||
|     ('x86_64', 'gcc', '4.9.3', '-march=x86-64 -mtune=generic'), | ||||
|     ('x86_64', 'gcc', '4.2.0', '-march=x86-64 -mtune=generic'), | ||||
|     ('x86_64', 'gcc', '4.1.1', '-march=x86-64 -mtune=x86-64'), | ||||
| @@ -198,6 +199,12 @@ def test_target_json_schema(): | ||||
|     ('nehalem', 'gcc', '4.9.3', '-march=nehalem -mtune=nehalem'), | ||||
|     ('nehalem', 'gcc', '4.8.5', '-march=corei7 -mtune=corei7'), | ||||
|     ('sandybridge', 'gcc', '4.8.5', '-march=corei7-avx -mtune=corei7-avx'), | ||||
|     # Test Clang / LLVM | ||||
|     ('sandybridge', 'clang', '3.9.0', '-march=x86-64 -mcpu=sandybridge'), | ||||
|     ('icelake', 'clang', '6.0.0', '-march=x86-64 -mcpu=icelake'), | ||||
|     ('icelake', 'clang', '8.0.0', '-march=x86-64 -mcpu=icelake-client'), | ||||
|     ('zen2', 'clang', '9.0.0', '-march=x86-64 -mcpu=znver2'), | ||||
|     ('power9le', 'clang', '8.0.0', '-march=ppc64le -mcpu=pwr9'), | ||||
|     # Test that an unknown compiler returns an empty string | ||||
|     ('sandybridge', 'unknown', '4.8.5', ''), | ||||
| ]) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user