1#!/usr/bin/env python
2
3# Tool for canonical RISC-V architecture string.
4# Copyright (C) 2011-2021 Free Software Foundation, Inc.
5# Contributed by Andrew Waterman (andrew@sifive.com).
6#
7# This file is part of GCC.
8#
9# GCC is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 3, or (at your option)
12# any later version.
13#
14# GCC is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with GCC; see the file COPYING3.  If not see
21# <http://www.gnu.org/licenses/>.
22
23
24from __future__ import print_function
25import sys
26import collections
27import itertools
28from functools import reduce
29
30
31CANONICAL_ORDER = "mafdgqlcbjtpvn"
32LONG_EXT_PREFIXES = ['z', 's', 'h', 'x']
33
34#
35# IMPLIED_EXT(ext) -> implied extension list.
36#
37IMPLIED_EXT = {
38  "d" : ["f"],
39}
40
41def arch_canonicalize(arch):
42  # TODO: Support extension version.
43  new_arch = ""
44  if arch[:5] in ['rv32e', 'rv32i', 'rv32g', 'rv64i', 'rv64g']:
45    # TODO: We should expand g to imad_zifencei once we support newer spec.
46    new_arch = arch[:5].replace("g", "imafd")
47  else:
48    raise Exception("Unexpected arch: `%s`" % arch[:5])
49
50  # Find any Z, S, H or X
51  long_ext_prefixes_idx = map(lambda x: arch.find(x), LONG_EXT_PREFIXES)
52
53  # Filter out any non-existent index.
54  long_ext_prefixes_idx = list(filter(lambda x: x != -1, long_ext_prefixes_idx))
55  if long_ext_prefixes_idx:
56    first_long_ext_idx = min(long_ext_prefixes_idx)
57    long_exts = arch[first_long_ext_idx:].split("_")
58    std_exts = list(arch[5:first_long_ext_idx])
59  else:
60    long_exts = []
61    std_exts = list(arch[5:])
62
63  #
64  # Handle implied extensions.
65  #
66  for ext in std_exts + long_exts:
67    if ext in IMPLIED_EXT:
68      implied_exts = IMPLIED_EXT[ext]
69      for implied_ext in implied_exts:
70        if implied_ext not in std_exts + long_exts:
71          long_exts.append(implied_ext)
72
73  # Single letter extension might appear in the long_exts list,
74  # becasue we just append extensions list to the arch string.
75  std_exts += list(filter(lambda x:len(x) == 1, long_exts))
76
77  def longext_sort (exts):
78    if not exts.startswith("zxm") and exts.startswith("z"):
79      # If "Z" extensions are named, they should be ordered first by CANONICAL.
80      if exts[1] not in CANONICAL_ORDER:
81        raise Exception("Unsupported extension `%s`" % exts)
82      canonical_sort = CANONICAL_ORDER.index(exts[1])
83    else:
84      canonical_sort = -1
85    return (exts.startswith("x"), exts.startswith("zxm"),
86            LONG_EXT_PREFIXES.index(exts[0]), canonical_sort, exts[1:])
87
88  # Multi-letter extension must be in lexicographic order.
89  long_exts = list(sorted(filter(lambda x:len(x) != 1, long_exts),
90                          key=longext_sort))
91
92  # Put extensions in canonical order.
93  for ext in CANONICAL_ORDER:
94    if ext in std_exts:
95      new_arch += ext
96
97  # Check every extension is processed.
98  for ext in std_exts:
99    if ext == '_':
100      continue
101    if ext not in CANONICAL_ORDER:
102      raise Exception("Unsupported extension `%s`" % ext)
103
104  # Concat rest of the multi-char extensions.
105  if long_exts:
106    new_arch += "_" + "_".join(long_exts)
107  return new_arch
108
109if len(sys.argv) < 2:
110  print ("Usage: %s <arch_str> [<arch_str>*]" % sys.argv)
111  sys.exit(1)
112
113for arg in sys.argv[1:]:
114  print (arch_canonicalize(arg))
115