blob: 8a1ba0cb96323810dc50c26468a77773b5b822ac [file] [log] [blame]
Ben Chengba4fc8b2009-06-01 13:00:29 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2007 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17#
18# Using instructions from an architecture-specific config file, generate C
19# and assembly source files for the Dalvik JIT.
20#
21
22import sys, string, re, time
23from string import Template
24
25interp_defs_file = "TemplateOpList.h" # need opcode list
26
27handler_size_bits = -1000
28handler_size_bytes = -1000
29in_op_start = 0 # 0=not started, 1=started, 2=ended
30default_op_dir = None
31opcode_locations = {}
32asm_stub_text = []
33label_prefix = ".L" # use ".L" to hide labels from gdb
34
35
36# Exception class.
37class DataParseError(SyntaxError):
38 "Failure when parsing data file"
39
40#
41# Set any omnipresent substitution values.
42#
43def getGlobalSubDict():
44 return { "handler_size_bits":handler_size_bits,
45 "handler_size_bytes":handler_size_bytes }
46
47#
48# Parse arch config file --
49# Set handler_size_bytes to the value of tokens[1], and handler_size_bits to
50# log2(handler_size_bytes). Throws an exception if "bytes" is not a power
51# of two.
52#
53def setHandlerSize(tokens):
54 global handler_size_bits, handler_size_bytes
55 if len(tokens) != 2:
56 raise DataParseError("handler-size requires one argument")
57 if handler_size_bits != -1000:
58 raise DataParseError("handler-size may only be set once")
59
60 # compute log2(n), and make sure n is a power of 2
61 handler_size_bytes = bytes = int(tokens[1])
62 bits = -1
63 while bytes > 0:
64 bytes //= 2 # halve with truncating division
65 bits += 1
66
67 if handler_size_bytes == 0 or handler_size_bytes != (1 << bits):
68 raise DataParseError("handler-size (%d) must be power of 2 and > 0" \
69 % orig_bytes)
70 handler_size_bits = bits
71
72#
73# Parse arch config file --
74# Copy a file in to the C or asm output file.
75#
76def importFile(tokens):
77 if len(tokens) != 2:
78 raise DataParseError("import requires one argument")
79 source = tokens[1]
80 if source.endswith(".S"):
81 appendSourceFile(tokens[1], getGlobalSubDict(), asm_fp, None)
82 else:
83 raise DataParseError("don't know how to import %s (expecting .c/.S)"
84 % source)
85
86#
87# Parse arch config file --
88# Copy a file in to the C or asm output file.
89#
90def setAsmStub(tokens):
91 global asm_stub_text
92 if len(tokens) != 2:
93 raise DataParseError("import requires one argument")
94 try:
95 stub_fp = open(tokens[1])
96 asm_stub_text = stub_fp.readlines()
97 except IOError, err:
98 stub_fp.close()
99 raise DataParseError("unable to load asm-stub: %s" % str(err))
100 stub_fp.close()
101
102#
103# Parse arch config file --
104# Start of opcode list.
105#
106def opStart(tokens):
107 global in_op_start
108 global default_op_dir
109 if len(tokens) != 2:
110 raise DataParseError("opStart takes a directory name argument")
111 if in_op_start != 0:
112 raise DataParseError("opStart can only be specified once")
113 default_op_dir = tokens[1]
114 in_op_start = 1
115
116#
117# Parse arch config file --
118# Set location of a single opcode's source file.
119#
120def opEntry(tokens):
121 #global opcode_locations
122 if len(tokens) != 3:
123 raise DataParseError("op requires exactly two arguments")
124 if in_op_start != 1:
125 raise DataParseError("op statements must be between opStart/opEnd")
126 try:
127 index = opcodes.index(tokens[1])
128 except ValueError:
129 raise DataParseError("unknown opcode %s" % tokens[1])
130 opcode_locations[tokens[1]] = tokens[2]
131
132#
133# Parse arch config file --
134# End of opcode list; emit instruction blocks.
135#
136def opEnd(tokens):
137 global in_op_start
138 if len(tokens) != 1:
139 raise DataParseError("opEnd takes no arguments")
140 if in_op_start != 1:
141 raise DataParseError("opEnd must follow opStart, and only appear once")
142 in_op_start = 2
143
144 loadAndEmitOpcodes()
145
146
147#
148# Extract an ordered list of instructions from the VM sources. We use the
149# "goto table" definition macro, which has exactly 256 entries.
150#
151def getOpcodeList():
152 opcodes = []
153 opcode_fp = open("%s/%s" % (target_arch, interp_defs_file))
154 opcode_re = re.compile(r"^JIT_TEMPLATE\((\w+)\)", re.DOTALL)
155 for line in opcode_fp:
156 match = opcode_re.match(line)
157 if not match:
158 continue
159 opcodes.append("TEMPLATE_" + match.group(1))
160 opcode_fp.close()
161
162 return opcodes
163
164
165#
166# Load and emit opcodes for all 256 instructions.
167#
168def loadAndEmitOpcodes():
169 sister_list = []
170
171 # point dvmAsmInstructionStart at the first handler or stub
172 asm_fp.write("\n .global dvmCompilerTemplateStart\n")
173 asm_fp.write(" .type dvmCompilerTemplateStart, %function\n")
174 asm_fp.write(" .text\n\n")
175 asm_fp.write("dvmCompilerTemplateStart:\n\n")
176
177 for i in xrange(len(opcodes)):
178 op = opcodes[i]
179
180 if opcode_locations.has_key(op):
181 location = opcode_locations[op]
182 else:
183 location = default_op_dir
184
185 loadAndEmitAsm(location, i, sister_list)
186
187 # Use variable sized handlers now
188 # asm_fp.write("\n .balign %d\n" % handler_size_bytes)
189 asm_fp.write(" .size dvmCompilerTemplateStart, .-dvmCompilerTemplateStart\n")
190
191#
192# Load an assembly fragment and emit it.
193#
194def loadAndEmitAsm(location, opindex, sister_list):
195 op = opcodes[opindex]
196 source = "%s/%s.S" % (location, op)
197 dict = getGlobalSubDict()
198 dict.update({ "opcode":op, "opnum":opindex })
199 print " emit %s --> asm" % source
200
201 emitAsmHeader(asm_fp, dict)
202 appendSourceFile(source, dict, asm_fp, sister_list)
203
204#
205# Output the alignment directive and label for an assembly piece.
206#
207def emitAsmHeader(outfp, dict):
208 outfp.write("/* ------------------------------ */\n")
209 # The alignment directive ensures that the handler occupies
210 # at least the correct amount of space. We don't try to deal
211 # with overflow here.
212 outfp.write(" .balign 4\n")
213 # Emit a label so that gdb will say the right thing. We prepend an
214 # underscore so the symbol name doesn't clash with the OpCode enum.
215 template_name = "dvmCompiler_%(opcode)s" % dict
216 outfp.write(" .global %s\n" % template_name);
217 outfp.write("%s:\n" % template_name);
218
219#
220# Output a generic instruction stub that updates the "glue" struct and
221# calls the C implementation.
222#
223def emitAsmStub(outfp, dict):
224 emitAsmHeader(outfp, dict)
225 for line in asm_stub_text:
226 templ = Template(line)
227 outfp.write(templ.substitute(dict))
228
229#
230# Append the file specified by "source" to the open "outfp". Each line will
231# be template-replaced using the substitution dictionary "dict".
232#
233# If the first line of the file starts with "%" it is taken as a directive.
234# A "%include" line contains a filename and, optionally, a Python-style
235# dictionary declaration with substitution strings. (This is implemented
236# with recursion.)
237#
238# If "sister_list" is provided, and we find a line that contains only "&",
239# all subsequent lines from the file will be appended to sister_list instead
240# of copied to the output.
241#
242# This may modify "dict".
243#
244def appendSourceFile(source, dict, outfp, sister_list):
245 outfp.write("/* File: %s */\n" % source)
246 infp = open(source, "r")
247 in_sister = False
248 for line in infp:
249 if line.startswith("%include"):
250 # Parse the "include" line
251 tokens = line.strip().split(' ', 2)
252 if len(tokens) < 2:
253 raise DataParseError("malformed %%include in %s" % source)
254
255 alt_source = tokens[1].strip("\"")
256 if alt_source == source:
257 raise DataParseError("self-referential %%include in %s"
258 % source)
259
260 new_dict = dict.copy()
261 if len(tokens) == 3:
262 new_dict.update(eval(tokens[2]))
263 #print " including src=%s dict=%s" % (alt_source, new_dict)
264 appendSourceFile(alt_source, new_dict, outfp, sister_list)
265 continue
266
267 elif line.startswith("%default"):
268 # copy keywords into dictionary
269 tokens = line.strip().split(' ', 1)
270 if len(tokens) < 2:
271 raise DataParseError("malformed %%default in %s" % source)
272 defaultValues = eval(tokens[1])
273 for entry in defaultValues:
274 dict.setdefault(entry, defaultValues[entry])
275 continue
276
277 elif line.startswith("%verify"):
278 # more to come, someday
279 continue
280
281 elif line.startswith("%break") and sister_list != None:
282 # allow more than one %break, ignoring all following the first
283 if not in_sister:
284 in_sister = True
285 sister_list.append("\n/* continuation for %(opcode)s */\n"%dict)
286 continue
287
288 # perform keyword substitution if a dictionary was provided
289 if dict != None:
290 templ = Template(line)
291 try:
292 subline = templ.substitute(dict)
293 except KeyError, err:
294 raise DataParseError("keyword substitution failed in %s: %s"
295 % (source, str(err)))
296 except:
297 print "ERROR: substitution failed: " + line
298 raise
299 else:
300 subline = line
301
302 # write output to appropriate file
303 if in_sister:
304 sister_list.append(subline)
305 else:
306 outfp.write(subline)
307 outfp.write("\n")
308 infp.close()
309
310#
311# Emit a C-style section header comment.
312#
313def emitSectionComment(str, fp):
314 equals = "========================================" \
315 "==================================="
316
317 fp.write("\n/*\n * %s\n * %s\n * %s\n */\n" %
318 (equals, str, equals))
319
320
321#
322# ===========================================================================
323# "main" code
324#
325
326#
327# Check args.
328#
329if len(sys.argv) != 3:
330 print "Usage: %s target-arch output-dir" % sys.argv[0]
331 sys.exit(2)
332
333target_arch = sys.argv[1]
334output_dir = sys.argv[2]
335
336#
337# Extract opcode list.
338#
339opcodes = getOpcodeList()
340#for op in opcodes:
341# print " %s" % op
342
343#
344# Open config file.
345#
346try:
347 config_fp = open("config-%s" % target_arch)
348except:
349 print "Unable to open config file 'config-%s'" % target_arch
350 sys.exit(1)
351
352#
353# Open and prepare output files.
354#
355try:
356 asm_fp = open("%s/CompilerTemplateAsm-%s.S" % (output_dir, target_arch), "w")
357except:
358 print "Unable to open output files"
359 print "Make sure directory '%s' exists and existing files are writable" \
360 % output_dir
361 # Ideally we'd remove the files to avoid confusing "make", but if they
362 # failed to open we probably won't be able to remove them either.
363 sys.exit(1)
364
365print "Generating %s" % (asm_fp.name)
366
367file_header = """/*
368 * This file was generated automatically by gen-template.py for '%s'.
369 *
370 * --> DO NOT EDIT <--
371 */
372
373""" % (target_arch)
374
375asm_fp.write(file_header)
376
377#
378# Process the config file.
379#
380failed = False
381try:
382 for line in config_fp:
383 line = line.strip() # remove CRLF, leading spaces
384 tokens = line.split(' ') # tokenize
385 #print "%d: %s" % (len(tokens), tokens)
386 if len(tokens[0]) == 0:
387 #print " blank"
388 pass
389 elif tokens[0][0] == '#':
390 #print " comment"
391 pass
392 else:
393 if tokens[0] == "handler-size":
394 setHandlerSize(tokens)
395 elif tokens[0] == "import":
396 importFile(tokens)
397 elif tokens[0] == "asm-stub":
398 setAsmStub(tokens)
399 elif tokens[0] == "op-start":
400 opStart(tokens)
401 elif tokens[0] == "op-end":
402 opEnd(tokens)
403 elif tokens[0] == "op":
404 opEntry(tokens)
405 else:
406 raise DataParseError, "unrecognized command '%s'" % tokens[0]
407except DataParseError, err:
408 print "Failed: " + str(err)
409 # TODO: remove output files so "make" doesn't get confused
410 failed = True
411 asm_fp.close()
412 c_fp = asm_fp = None
413
414config_fp.close()
415
416#
417# Done!
418#
419if asm_fp:
420 asm_fp.close()
421
422sys.exit(failed)