[interp] Small fixes (#11667)
[mono-project.git] / mono / mini / genmdesc.py
blob624088944bb22e1cc072a82f50a1220a39db9676
1 #!/usr/bin/env python
4 # This tool is used to generate the cpu-<ARCH>.h files used by the JIT. The input is the
5 # cpu-<ARCH>.md file, along with the instruction metadata in mini-ops.h.
8 import sys
9 import os
10 import re
12 # Keep it in sync with mini.h
13 MONO_INST_DEST = 0
14 MONO_INST_SRC1 = 1
15 MONO_INST_SRC2 = 2
16 MONO_INST_SRC3 = 3
17 MONO_INST_LEN = 4
18 MONO_INST_CLOB = 5
19 MONO_INST_MAX = 6
21 allowed_defines = { "TARGET_X86" : 1,
22 "TARGET_AMD64" : 1,
23 "TARGET_ARM" : 1,
24 "TARGET_ARM64" : 1,
25 "TARGET_POWERPC" : 1,
26 "TARGET_SPARC" : 1,
27 "TARGET_S390X" : 1,
28 "TARGET_MIPS" : 1,
29 "TARGET_RISCV" : 1,
30 "TARGET_RISCV32" : 1,
31 "TARGET_RISCV64" : 1,
32 "TARGET_WASM" : 1
35 class OpDef:
36 def __init__ (self, num, name, dest_def, src1_def, src2_def):
37 # num is the opcode value/index
38 self.num = num
39 self.name = name
40 self.dest_def = dest_def
41 self.src1_def = src1_def
42 self.src2_def = src2_def
43 self.used = False
44 # Data read from the cpu descriptor file
45 self.spec = ["", "", "", "", "", "", "", "", "", ""]
46 self.desc_idx = 0
48 def usage ():
49 print ("Usage: genmdesc.py <target define> <srcdir> <output file name> <c symbol name> <input file name>")
51 def parse_mini_ops (target_define):
52 opcodes = {}
53 opcode_id = 0
54 enabled = [target_define]
55 is_enabled = True
56 opcode_file = open (os.path.join (srcdir, "mini-ops.h"))
58 # Implement a subset of a c preprocessor, only handling #ifdef/#endif directives
60 for line in opcode_file:
61 line = line.strip ()
62 # print ("{0} {1}".format (line, is_enabled))
63 m = re.search (r'^\s*#if (.+)', line)
64 # FIXME: Check list of defines against an allowed list
65 if m != None:
66 is_enabled = False
67 parts = m.group (1).split ("||")
68 for part in parts:
69 part = part.strip ()
70 m = re.search (r'defined\((.+)\)', part)
71 if m == None:
72 print ("Unknown #ifdef line {0}".format (line))
73 exit (1)
74 define = m.group (1)
75 # Check that the file only contains TARGET_... defines
76 if not define in allowed_defines:
77 print ("Unknown define '{0}' in mini-ops.h".format (define))
78 exit (1)
79 for d in enabled:
80 if d == define:
81 is_enabled = True
82 elif line == "#endif":
83 is_enabled = True
84 else:
85 if is_enabled and line.startswith ("MINI_OP"):
86 m = re.search (r"MINI_OP\(\w+\s*\,\s*\"([^\"]+)\", (\w+), (\w+), (\w+)\)", line)
87 if m != None:
88 opcodes [m.group (1)] = OpDef(opcode_id, m.group (1), m.group (2), m.group (3), m.group (4))
89 else:
90 m = re.search (r"MINI_OP3\(\w+\s*\,\s*\"([^\"]+)\", (\w+), (\w+), (\w+), (\w+)\)", line)
91 if m != None:
92 opcodes [m.group (1)] = OpDef(opcode_id, m.group (1), m.group (2), m.group (3), m.group (4))
93 else:
94 print ("Unable to parse line: '{0}'".format (line))
95 exit (1)
96 opcode_id += 1
97 opcode_file.close ()
98 return opcodes
100 def parse_input(infile, opcodes):
102 # Comments are pound sign to end of string.
103 remove_comments = re.compile ("#.*")
105 for line in infile:
106 line = line.strip ()
107 # remove comments
108 line = re.sub (remove_comments, "", line)
110 # Ignore empty lines -- including it was just a comment.
111 if line == "":
112 continue
113 # Lines look like:
114 # expand_i2: dest:x src1:i len:18
115 parts = line.split (" ")
116 op_name = parts [0][:-1]
117 if not op_name in opcodes:
118 print ("Unknown opcode '{0}'".format (op_name))
119 exit (1)
120 opcode = opcodes [op_name]
121 opcode.used = True
122 for part in parts[1:]:
123 part = part.strip ()
124 if part == "":
125 continue
126 [spec, value] = part.split (":")
127 if spec == "dest":
128 if opcode.dest_def == "NONE":
129 print ("Inconsistent dreg for opcode '{0}'".format (op_name))
130 opcode.spec [MONO_INST_DEST] = value
131 elif spec == "src1":
132 if opcode.src1_def == "NONE":
133 print ("Inconsistent src1 for opcode '{0}'".format (op_name))
134 opcode.spec [MONO_INST_SRC1] = value
135 elif spec == "src2":
136 if opcode.src2_def == "NONE":
137 print ("Inconsistent src2 for opcode '{0}'".format (op_name))
138 opcode.spec [MONO_INST_SRC2] = value
139 elif spec == "src3":
140 opcode.spec [MONO_INST_SRC3] = value
141 elif spec == "len":
142 opcode.spec [MONO_INST_LEN] = chr(int(value))
143 elif spec == "clob":
144 opcode.spec [MONO_INST_CLOB] = value
145 else:
146 print ("Unknown specifier '{0}' for opcode '{0}'".format (spec))
147 exit (1)
149 def gen_output(f, opcodes):
150 sorted_opcodes = []
151 for op in opcodes.values ():
152 sorted_opcodes.append (op)
153 sorted_opcodes.sort (key=lambda op: op.num)
155 f.write ("/* File automatically generated by genmdesc.py, don't change */\n\n")
157 # Write desc table
158 f.write ("const char mono_{0} [] = {{\n".format (symbol_name))
159 f.write ("\t\"")
160 for i in range(MONO_INST_MAX):
161 f.write (r"\x0")
162 f.write ("\"\t/* null entry */\n")
163 idx = 1
164 for op in sorted_opcodes:
165 if not op.used:
166 continue
167 try:
168 f.write ("\t\"")
169 for c in op.spec[:MONO_INST_MAX]:
170 if c == "":
171 f.write (r"\x0")
172 f.write ("\" \"")
173 elif c.isalnum () and ord (c) < 0x80:
174 f.write (c)
175 else:
176 f.write (r"\x{0:x}".format (ord (c)))
177 f.write ("\" \"")
178 f.write ("\"\t/* {0} */\n".format (op.name))
179 op.desc_idx = idx * MONO_INST_MAX
180 idx += 1
181 except:
182 print ("Error emitting opcode '{0}': '{1}'.".format (op.name, sys.exc_info()))
183 f.write ("};\n")
185 # Write index table
186 f.write ("const guint16 mono_{0}_idx [] = {{\n".format (symbol_name))
187 for op in sorted_opcodes:
188 if not op.used:
189 f.write ("\t0,\t/* {0} */\n".format (op.name))
190 else:
191 f.write ("\t{0},\t/* {1} */\n".format (op.desc_idx, op.name))
192 f.write ("};\n\n")
195 # MAIN
198 if len (sys.argv) != 6:
199 usage ()
200 exit (1)
202 target_define = sys.argv [1]
203 srcdir = sys.argv [2]
204 outfile_name = sys.argv [3]
205 symbol_name = sys.argv [4]
206 infile_name = sys.argv [5]
208 # Parse mini-ops.h file for opcode metadata
209 opcodes = parse_mini_ops(target_define)
211 # Parse input file
212 infile = open (infile_name, 'r')
213 parse_input (infile, opcodes)
215 # Generate output
216 f = open (outfile_name, 'w')
217 gen_output (f, opcodes)
218 f.close ()