| #!/usr/bin/env python3 | |
| # RISC-V multilib list generator. | |
| # Copyright (C) 2011-2026 Free Software Foundation, Inc. | |
| # Contributed by Andrew Waterman (andrew@sifive.com). | |
| # | |
| # This file is part of GCC. | |
| # | |
| # GCC is free software; you can redistribute it and/or modify | |
| # it under the terms of the GNU General Public License as published by | |
| # the Free Software Foundation; either version 3, or (at your option) | |
| # any later version. | |
| # | |
| # GCC is distributed in the hope that it will be useful, | |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| # GNU General Public License for more details. | |
| # | |
| # You should have received a copy of the GNU General Public License | |
| # along with GCC; see the file COPYING3. If not see | |
| # <http://www.gnu.org/licenses/>. | |
| # Each argument to this script is of the form | |
| # <primary arch>-<abi>-<additional arches>-<extensions> | |
| # Example 1: | |
| # rv32imafd-ilp32d-rv32g-c,v | |
| # means that, in addition to rv32imafd, these configurations can also use the | |
| # rv32imafd-ilp32d libraries: rv32imafdc, rv32imafdv, rv32g, rv32gc, rv32gv | |
| # | |
| # Example 2: | |
| # rv32imafd-ilp32d--c*b | |
| # means that, in addition to rv32imafd, these configurations can also use the | |
| # rv32imafd-ilp32d libraries: rv32imafdc-ilp32d, rv32imafdb-ilp32d, | |
| # rv32imafdcb-ilp32d | |
| from __future__ import print_function | |
| import sys | |
| import os | |
| import collections | |
| import itertools | |
| from functools import reduce | |
| import subprocess | |
| import argparse | |
| # | |
| # TODO: Add test for this script. | |
| # | |
| SUPPORTED_ISA_SPEC = ["2.2", "20190608", "20191213"] | |
| arches = collections.OrderedDict() | |
| abis = collections.OrderedDict() | |
| required = [] | |
| reuse = [] | |
| extra_multlib_option_dict = collections.OrderedDict() | |
| def arch_canonicalize(arch, isa_spec): | |
| this_file = os.path.abspath(os.path.join( __file__)) | |
| arch_can_script = \ | |
| os.path.join(os.path.dirname(this_file), "arch-canonicalize") | |
| proc = subprocess.Popen([sys.executable, arch_can_script, | |
| '-misa-spec=%s' % isa_spec, arch], | |
| stdout=subprocess.PIPE) | |
| out, err = proc.communicate() | |
| return out.decode().strip() | |
| # | |
| # Handle expansion operation. | |
| # | |
| # e.g. "a*b" -> [("a",), ("b",), ("a", "b")] | |
| # "a" -> [("a",)] | |
| # | |
| def _expand_combination(ext): | |
| exts = list(ext.split("*")) | |
| # Add underline to every extension. | |
| # e.g. | |
| # _b * zvamo => _b * _zvamo | |
| exts = list(map(lambda x: '_' + x, exts)) | |
| # No need to expand if there is no `*`. | |
| if len(exts) == 1: | |
| return [(exts[0],)] | |
| # Generate combination! | |
| ext_combs = [] | |
| for comb_len in range(1, len(exts)+1): | |
| for ext_comb in itertools.combinations(exts, comb_len): | |
| ext_combs.append(ext_comb) | |
| return ext_combs | |
| # | |
| # Input a list and drop duplicated entry. | |
| # e.g. | |
| # ["a", "b", "ab", "a"] -> ["a", "b", "ab"] | |
| # | |
| def unique(x): | |
| # | |
| # Drop duplicated entry. | |
| # Convert list to set and then convert back to list. | |
| # | |
| # Add sorted to prevent non-deterministic results in different env. | |
| # | |
| return list(sorted(list(set(x)))) | |
| # | |
| # Expand EXT string if there is any expansion operator (*). | |
| # e.g. | |
| # "a*b,c" -> ["a", "b", "ab", "c"] | |
| # | |
| def expand_combination(ext): | |
| ext = list(filter(None, ext.split(','))) | |
| # Expand combination for EXT, got lots of list. | |
| # e.g. | |
| # a * b => [[("a",), ("b",)], [("a", "b")]] | |
| ext_combs = list(map(_expand_combination, ext)) | |
| # Then fold to single list. | |
| # e.g. | |
| # [[("a",), ("b",)], [("a", "b")]] => [("a",), ("b",), ("a", "b")] | |
| ext = list(reduce(lambda x, y: x + y, ext_combs, [])) | |
| # Fold the tuple to string. | |
| # e.g. | |
| # [("a",), ("b",), ("a", "b")] => ["a", "b", "ab"] | |
| ext = map(lambda e : reduce(lambda x, y: x + y, e), ext) | |
| # Drop duplicated entry. | |
| ext = unique(ext) | |
| return ext | |
| multilib_cfgs = filter(lambda x:not x.startswith("--"), sys.argv[1:]) | |
| options = filter(lambda x:x.startswith("--"), sys.argv[1:]) | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--cmodel", type=str) | |
| parser.add_argument('-misa-spec', type=str, | |
| default='20191213', | |
| choices=SUPPORTED_ISA_SPEC) | |
| parser.add_argument("cfgs", type=str, nargs='*') | |
| args = parser.parse_args() | |
| if args.cmodel: | |
| cmodels = [None] + args.cmodel.split(",") | |
| for cmodel in cmodels[1:]: | |
| extra_multlib_option_dict['mcmodel=%s' % cmodel] = cmodel | |
| else: | |
| cmodels = [None] | |
| for cmodel in cmodels: | |
| for cfg in args.cfgs: | |
| local_extra_multlib_option_dict = collections.OrderedDict() | |
| try: | |
| if cfg.count('-') == 3: | |
| (arch, abi, extra, ext) = cfg.split('-') | |
| extra_multlib_options = [] | |
| elif cfg.count('-') == 4: | |
| (arch, abi, extra, ext, extra_multlib_option) = cfg.split('-') | |
| # <extra-multilib-option> is a list of options separated by slash, and | |
| # each option is consisted of option name and multi-lib dir separated by | |
| # semicolon, the dash within the option should rewrite as underscore to | |
| # prevent confusion of parsing. | |
| # e.g. | |
| # fcf_protection=full/cfi : -fcf-protection=full as option name and cfi | |
| # as multilib path. | |
| extra_multlib_options = extra_multlib_option.split(',') | |
| for option in extra_multlib_options: | |
| option = option.replace('_', '-') | |
| option = option.split('/') | |
| local_extra_multlib_option_dict[option[0]] = option[1] | |
| extra_multlib_option_dict.update(local_extra_multlib_option_dict) | |
| except: | |
| print ("Invalid configure string %s, <arch>-<abi>-<extra>-<extensions>" | |
| "[-<extra-multilib-option>]\n" | |
| "<extra> and <extensions> can be empty, " | |
| "e.g. rv32imafd-ilp32--" % cfg) | |
| sys.exit(1) | |
| # Compact and large code model only support rv64. | |
| if cmodel in ["compact", "large"] and arch.startswith("rv32"): | |
| continue | |
| elif cmodel: | |
| local_extra_multlib_option_dict['mcmodel=%s' % cmodel] = cmodel | |
| arch = arch_canonicalize (arch, args.misa_spec) | |
| arches[arch] = 1 | |
| abis[abi] = 1 | |
| extra = list(filter(None, extra.split(','))) | |
| ext_combs = expand_combination(ext) | |
| alts = sum([[x] + [x + y for y in ext_combs] for x in [arch] + extra], []) | |
| alts = filter(lambda x: len(x) != 0, alts) | |
| alts = list(map(lambda a : arch_canonicalize(a, args.misa_spec), alts)) | |
| # Drop duplicated entry. | |
| alts = unique(alts) | |
| for alt in alts: | |
| if alt == arch: | |
| continue | |
| arches[alt] = 1 | |
| reuse.append('march.%s/mabi.%s=march.%s/mabi.%s' % (arch, abi, alt, abi)) | |
| extra_multlib_option_str = '/'.join(local_extra_multlib_option_dict.keys()) | |
| multilib_cfg_line = 'march=%s/mabi=%s' % (arch, abi) | |
| if (len(extra_multlib_option_str) > 0): | |
| multilib_cfg_line += '/' + extra_multlib_option_str | |
| required.append(multilib_cfg_line) | |
| arch_options = '/'.join(['march=%s' % x for x in arches.keys()]) | |
| arch_dirnames = ' \\\n'.join(arches.keys()) | |
| abi_options = '/'.join(['mabi=%s' % x for x in abis.keys()]) | |
| abi_dirnames = ' \\\n'.join(abis.keys()) | |
| prog = sys.argv[0].split('/')[-1] | |
| print('# This file was generated by %s with the command:' % prog) | |
| print('# %s' % ' '.join(sys.argv)) | |
| print('MULTILIB_OPTIONS = %s %s %s' % (arch_options, abi_options, " ".join(extra_multlib_option_dict.keys()))) | |
| print('MULTILIB_DIRNAMES = %s %s %s' % (arch_dirnames, abi_dirnames, " ".join(extra_multlib_option_dict.values()))) | |
| print('MULTILIB_REQUIRED = %s' % ' \\\n'.join(required)) | |
| print('MULTILIB_REUSE = %s' % ' \\\n'.join(reuse)) |