hbmap: fix iterator truncation when size_t < 32bit
[rofl0r-agsutils.git] / agsoptimize
blobc78e65a8dc1b399719b138f79af161e16c61df62
1 #!/usr/bin/env python2
3 # the AGS script compiler basically doesn't optimize at all,
4 # and the code it emits is super-naive and redundant.
5 # the register bx is most often used only as a temporary
6 # storage and discarded immediately after doing one basic op.
7 # therefore, doing these transformations should be safe.
8 # running these transformations on .s files reduces the
9 # amount of code by about 15-25%, but it also removes debugging
10 # information (sourceline directives).
11 # this speeds up game execution and makes the game smaller.
12 # currently, the optimizer takes input only from stdin and
13 # apply a single transformation at a time.
14 # a future optimization could be to cache code-chunks between
15 # jump labels and apply multiple transformation on the in-memory
16 # code.
18 import sys, re
20 class MultiLineMatcher():
21         def __init__(self, regexes, matchfn, nomatchfn, fout=sys.stdout):
22                 self.matchfn = matchfn
23                 self.nomatchfn = nomatchfn
24                 self.regexes = regexes
25                 self.line_matches = 0
26                 self.saved_lines = None
27                 self.matches = None
28                 self.fout = fout
30         def feed(self, line):
31                 line = line.rstrip('\n')
32                 m = self.regexes[self.line_matches].match(line)
33                 if m:
34                         if self.matches is None: self.matches = []
35                         if self.saved_lines is None: self.saved_lines = []
36                         self.matches.append(m)
37                         self.saved_lines.append(line)
38                         self.line_matches += 1
39                         if self.line_matches == len(self.regexes):
40                                 self.matchfn(self, self.matches, self.saved_lines)
41                                 self.matches = None
42                                 self.line_matches = 0
43                                 self.saved_lines = None
44                 else:
45                         self.line_matches = 0
46                         self.matches = None
47                         if self.saved_lines:
48                                 for ln in self.saved_lines:
49                                         self.nomatchfn(self, ln)
50                                 self.saved_lines = None
51                         self.nomatchfn(self, line)
53 removed = 0
54 lineno = 0
56 def push_pop_matchfn(matcher, matches, lines):
57         ws, reg1 = matches[0].groups(0)
58         ws2, reg2 = matches[1].groups(0)
59         global removed
60         if reg1 == reg2: removed += 1
61         else: matcher.fout.write("%smr %s, %s\n"%(ws, reg2, reg1))
62         removed += 1
64 def output_fn(matcher, line):
65         matcher.fout.write( "%s\n" %  line)
67 def sourceline_matchfn(matcher, matches, lines):
68         global removed
69         removed += 1
71 def thisaddr_sourceline_matchfn(matcher, matches, lines):
72         global removed
73         ws, addr = matches[0].groups(0)
74         matcher.fout.write( "%s\n" % lines[0])
75         matcher.fout.write( "%ssourceline %d\n" %(ws, int(addr)))
76         removed += 1
78 def cmp_mr_matchfn(matcher, matches, lines):
79         global removed
80         ws, op = matches[0].groups(0)
81         matcher.fout.write( "%s%s ax, bx\n"%(ws, op))
82         removed += 1
84 def cmp2_matchfn(matcher, matches, lines):
85         def reverse_cmp(op):
86                 if op == 'gt': return 'lte'
87                 elif op == 'gte': return 'lt'
88                 elif op == 'lt': return 'gte'
89                 elif op == 'lte': return 'gt'
90         global removed
91         ws, val = matches[1].groups(0)
92         ws2, op = matches[2].groups(0)
93         matcher.fout.write("%sli bx, %s\n"%(ws, val))
94         matcher.fout.write("%s%s ax, bx\n"%(ws, op)) # since we already switched registers, we don't need to switch the op too
95         removed += 2
97 def load_negative_literal_matchfn(matcher, matches, lines):
98         global removed
99         ws, val = matches[1].groups(0)
100         matcher.fout.write( "%smr bx, ax\n"%(ws))
101         matcher.fout.write( "%sli ax, -%s\n"%(ws, val))
102         removed += 4
104 def load_negative_literal2_matchfn(matcher, matches, lines):
105         global removed
106         ws, val = matches[0].groups(0)
107         matcher.fout.write( "%sli ax, -%s\n"%(ws, val))
108         matcher.fout.write( "%sli bx, -%s\n"%(ws, val))
109         removed += 2
111 def load_literal_matchfn(matcher, matches, lines):
112         global removed
113         ws, val = matches[1].groups(0)
114         matcher.fout.write( "%smr bx, ax\n"%(ws))
115         matcher.fout.write( "%sli ax, %s\n"%(ws, val))
116         removed += 1
118 def axmar_matchfn(matcher, matches, lines):
119         global removed
120         ws, val = matches[0].groups(0)
121         matcher.fout.write( "%sli bx, %s\n"%(ws, val))
122         matcher.fout.write( "%s\n" % lines[2])
123         matcher.fout.write( "%s\n" % lines[3])
124         removed += 1
126 def mr_swap_matchfn(matcher, matches, lines):
127         global removed
128         matcher.fout.write( "%s\n" % lines[0])
129         removed += 1
131 def memread4_swap_matchfn(matcher, matches, lines):
132         global removed
133         # # memread4 ax; mr bx, ax; li ax, 1
134         ws, reg1 = matches[0].groups(0)
135         ws, reg2a, reg2b = matches[1].groups(0)
136         ws, reg3, val = matches[2].groups(0)
137         if reg1 == reg2b and reg1 == reg3:
138                 matcher.fout.write("%smemread4 %s\n"%(ws, reg2a))
139                 matcher.fout.write("%sli %s, %s\n"%(ws, reg1, val))
140                 removed += 1
141         else:
142                 for line in lines: matcher.fout.write("%s\n"%line)
144 def ptrstack2x_matchfn(matcher, matches, lines):
145         global removed
146         ws, val1 = matches[0].groups(0)
147         ws, val2 = matches[2].groups(0)
148         if val1 == val2:
149                 matcher.fout.write( "%s\n" % lines[0])
150                 matcher.fout.write( "%s\n" % lines[1])
151                 removed += 1
152         else:
153                 for line in lines: matcher.fout.write("%s\n"%line)
155 def regload_arith_matchfn(matcher, matches, lines):
156         # li ax, 1; add bx, ax, ax; mr ax, bx
157         global removed
158         ws, reg1, val = matches[0].groups(0)
159         ws, op, reg2a, reg2b = matches[1].groups(0)
160         ws, reg3a, reg3b = matches[2].groups(0)
161         if reg1 == reg2b and reg3a == reg1 and reg2a == reg3b:
162                 matcher.fout.write( "%s%si %s, %s\n" % (ws, op, reg2a, val))
163                 matcher.fout.write( "%s\n" % lines[2])
164                 removed += 1
165         else:
166                 for line in lines: matcher.fout.write("%s\n"%line)
168 def load0_matchfn(matcher, matches, lines):
169         global removed
170         ws, reg1 = matches[0].groups(0)
171         matcher.fout.write( "%sxor %s, %s\n"%(ws, reg1, reg1))
172         # actually we don't remove here, but we wanna see the count of replacements
173         removed += 1
175 # ========= macro replacement ==========
176 def objcall0_matchfn(matcher, matches, lines):
177         ws, obj, tail = matches[0].groups(0)
178         ws, func, tail = matches[5].groups(0)
179         global removed
180         matcher.fout.write("%sOBJCALL0(%s, %s)%s\n"%(ws, func, obj, tail))
181         removed += 1
183 def objcall1_matchfn(matcher, matches, lines):
184         ws, arg, tail = matches[0].groups(0)
185         ws, obj, tail = matches[1].groups(0)
186         ws, func, tail = matches[7].groups(0)
187         global removed
188         matcher.fout.write("%sOBJCALL1(%s, %s, %s)%s\n"%(ws, func, obj, arg, tail))
189         removed += 10
191 def objcall1dynstr_matchfn(matcher, matches, lines):
192         ws, arg, tail = matches[0].groups(0)
193         ws, obj, tail = matches[3].groups(0)
194         ws, func, tail = matches[9].groups(0)
195         global removed
196         matcher.fout.write("%sOBJCALL1_DYNSTR(%s, %s, %s)%s\n"%(ws, func, obj, arg, tail))
197         removed += 12
199 def farcall0_matchfn(matcher, matches, lines):
200         ws, fun, tail = matches[1].groups(0)
201         global removed
202         removed += 1
203         matcher.fout.write("%sFARCALL0(%s)%s\n"%(ws, fun, tail))
205 def farcall1_matchfn(matcher, matches, lines):
206         ws, arg, tail = matches[0].groups(0)
207         ws, fun, tail = matches[3].groups(0)
208         global removed
209         removed += 1
210         matcher.fout.write("%sFARCALL1(%s, %s)%s\n"%(ws, fun, arg, tail))
212 def varislit_matchfn(matcher, matches, lines):
213         ws, var, tail = matches[0].groups(0)
214         ws, val, tail = matches[3].groups(0)
215         ws, op, tail_ = matches[4].groups(0)
216         global removed
217         removed += 2
218         m = "VAR_EQ" if op == 'cmpeq' else "VAR_NE"
219         matcher.fout.write("%s%s(%s, %s)%s\n"%(ws, m, var, val, tail))
221 def varislit2_matchfn(matcher, matches, lines):
222         ws, var, tail = matches[0].groups(0)
223         ws, val, tail = matches[2].groups(0)
224         ws, op, tail_ = matches[3].groups(0)
225         global removed
226         removed += 1
227         m = "VAR_EQ" if op == 'cmpeq' else "VAR_NE"
228         matcher.fout.write("%s%s(%s, %s)%s\n"%(ws, m, var, val, tail))
230 def incvar_matchfn(matcher, matches, lines):
231         ws, val, tail = matches[0].groups(0)
232         ws, var1, tail = matches[2].groups(0)
233         ws, op, tail = matches[5].groups(0)
234         ws, var2, tail = matches[6].groups(0)
235         global removed
236         if var1 != var2:
237                 for line in lines: matcher.fout.write("%s\n"%line)
238         else:
239                 removed += 1
240                 vali = int(val)
241                 if op == "sub":
242                         vali *= -1
243                 matcher.fout.write("%sINC_VAR(%s, %d)%s\n"%(ws, var1, vali, tail))
245 def incvar2_matchfn(matcher, matches, lines):
246         ws, var, tail = matches[0].groups(0)
247         ws, op, val, tail = matches[2].groups(0)
248         global removed
249         removed += 1
250         vali = int(val)
251         if op == "subi":
252                 vali *= -1
253         matcher.fout.write("%sINC_VAR(%s, %d)%s\n"%(ws, var, vali, tail))
255 def setvar_matchfn(matcher, matches, lines):
256         ws, val, tail = matches[0].groups(0)
257         ws, var, tail = matches[1].groups(0)
258         global removed
259         removed += 1
260         matcher.fout.write("%sSET_VAR(%s, %s)%s\n"%(ws, var, val, tail))
263 def eprint(text): sys.stderr.write("%s\n"%text)
265 def usage():
266         eprint("usage: %s [options] infile outfile"%sys.argv[0])
267         eprint("")
268         eprint("options: -cmp -pushpop -sourceline -lnl -ll -cmp2 -axmar -mrswap -m4s ...")
269         eprint("at least one option required.\n")
270         for i in commandline_args_matcher_map.keys():
271                 eprint("%s: %s"%(i, help_text[i]))
272         return 1
275 def get_matcher_name(matcher):
276         return matcher_names[matcher]
278 removed_per_matcher = {}
279 def optimize(matcher, fin):
280         global lineno
281         global removed
282         removed = 0
283         while True:
284                 lineno = lineno + 1
285                 s = fin.readline()
286                 if s == '': break
287                 matcher.feed(s)
288         mn = get_matcher_name(matcher)
289         global removed_per_matcher
290         removed_per_matcher[mn] = removed
292 def seek_text(fin, fout):
293         global lineno
294         while True:
295                 lineno = lineno + 1
296                 s = fin.readline()
297                 if s == '': break
298                 fout.write(s)
299                 if s.startswith('.text'): break
301 all_matchers = {}
302 matcher_names = {}
304 re_tail = r"(| \\)$"
305 re_digits = r"([0-9]+)"
306 re_number__ = r"-{0,1}[0-9]+"
307 re_number = r"(" + re_number__ + ")"
308 re_string__ = r'\"[^"]*\"'
309 re_string = r'(' + re_string__ + ')'
310 # variable prefixed with @ means: variable exported by curr script
311 re_var__ = r"@{0,1}[_9A-Za-z]+[0-9A-Za-z_]*"
312 re_var = r"(" + re_var__ + ")"
313 re_var_str_or_num = "(" + "(?:" + re_var__ + ")|(?:" + re_string__ + ")|(?:" + re_number__ +")"  + ")"
314 re_iden_or_num = r"([0-9A-Za-z_]+)"
315 re_leading_ws = r"(\s*)"
316 re_gpr = r"([a-d]x)"
317 re_extfunc = r"([_9A-Za-z]+[0-9A-Za-z_:]*)"
319 def prep_matchers():
320         global all_matchers
321         global matcher_names
322         all_matchers['pushpop_matcher'] = MultiLineMatcher([
323                 re.compile('(\s+)push ([a-z]+)'),
324                 re.compile('(\s+)pop ([a-z]+)'),
325         ], push_pop_matchfn, output_fn)
327         all_matchers['sourceline_matcher'] = MultiLineMatcher([
328                 re.compile('(\s+)sourceline ([0-9]+)'),
329         ], sourceline_matchfn, output_fn)
331         # this matcher is only for debug purposes.
332         # it adds a sourceline statement after every thisaddr,
333         # which is right at the beginning of each func.
334         # so one can set e.g. a breakpoint in the ags interpreter.
335         all_matchers['thisaddr_sourceline_matcher'] = MultiLineMatcher([
336                 re.compile('(\s+)thisaddr ([0-9]+)$'),
337         ], thisaddr_sourceline_matchfn, output_fn)
339         all_matchers['cmp_mr_matcher'] = MultiLineMatcher([
340                 re.compile('(\s+)(cmpeq|cmpne|lor|land) bx, ax'),
341                 re.compile('(\s+)mr ax, bx'),
342         ], cmp_mr_matchfn, output_fn)
344         all_matchers['load_negative_literal_matcher'] = MultiLineMatcher([
345                 re.compile('(\s+)push ax$'),
346                 re.compile('(\s+)li ax, ([0-9]+)$'),
347                 re.compile('(\s+)li bx, 0$'),
348                 re.compile('(\s+)sub bx, ax$'),
349                 re.compile('(\s+)mr ax, bx$'),
350                 re.compile('(\s+)pop bx$'),
351         ], load_negative_literal_matchfn, output_fn)
353         all_matchers['load_negative_literal2_matcher'] = MultiLineMatcher([
354                 re.compile('(\s+)li ax, ([0-9]+)$'),
355                 re.compile('(\s+)li bx, 0$'),
356                 re.compile('(\s+)sub bx, ax$'),
357                 re.compile('(\s+)mr ax, bx$'),
358         ], load_negative_literal2_matchfn, output_fn)
360         all_matchers['load_literal_matcher'] = MultiLineMatcher([
361                 re.compile('(\s+)push ax$'),
362                 re.compile('(\s+)li ax, ([0-9]+)$'),
363                 re.compile('(\s+)pop bx$'),
364         ], load_literal_matchfn, output_fn)
366         all_matchers['cmp2_matcher'] = MultiLineMatcher([
367                 re.compile('(\s+)mr bx, ax'),
368                 re.compile('(\s+)li ax, ([0-9]+)'),
369                 re.compile('(\s+)(gt|gte|lt|lte) bx, ax'),
370                 re.compile('(\s+)mr ax, bx'),
371         ], cmp2_matchfn, output_fn)
373         all_matchers['axmar_matcher'] = MultiLineMatcher([
374                 re.compile('(\s+)li ax, ([0-9]+)$'),
375                 re.compile('(\s+)mr bx, ax$'),
376                 re.compile('(\s+)li mar, (.+)$'),
377                 re.compile('(\s+)mr ax, mar$'),
378         ], axmar_matchfn, output_fn)
380         all_matchers['mr_swap_matcher'] = MultiLineMatcher([
381                 re.compile('(\s+)mr ax, bx'),
382                 re.compile('(\s+)mr bx, ax'),
383         ], mr_swap_matchfn, output_fn)
385         all_matchers['memread4_swap_matcher'] = MultiLineMatcher([
386         # memread4 ax; mr bx, ax; li ax, 1
387                 re.compile('(\s+)memread4 ([a-d]x)'),
388                 re.compile('(\s+)mr ([a-d]x), ([a-d]x)'),
389                 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
390         ], memread4_swap_matchfn, output_fn)
392         all_matchers['ptrstack2x_matcher'] = MultiLineMatcher([
393                 re.compile('(\s+)ptrstack ([0-9]+)$'),
394                 re.compile('(\s+)mem.*$'),
395                 re.compile('(\s+)ptrstack ([0-9]+)$'),
396         ], ptrstack2x_matchfn, output_fn)
398         all_matchers['regload_arith_matcher'] = MultiLineMatcher([
399         # li ax, 1; add bx, ax, ax; mr ax, bx
400                 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
401                 re.compile('(\s+)(add|sub) ([a-d]x), ([a-d]x)$'),
402                 re.compile('(\s+)mr ([a-d]x), ([a-d]x)$'),
403         ], regload_arith_matchfn, output_fn)
405         all_matchers['assertlte_matcher'] = MultiLineMatcher([
406                 re.compile('(\s+)assertlte\s.*'),
407         ], sourceline_matchfn, output_fn)
409         all_matchers['load0_matcher'] = MultiLineMatcher([
410                 re.compile('(\s+)li ([a-d]x), 0$'),
411         ], load0_matchfn, output_fn)
413         # macros. we replace specific snippets with macros, to make the code
414         # more readable.
416         all_matchers['farcall0_matcher'] = MultiLineMatcher([
417                 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
418                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
419                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
420         ], farcall0_matchfn, output_fn)
422         all_matchers['farcall1_matcher'] = MultiLineMatcher([
423                 re.compile(re_leading_ws + r'li ax, ' + re_var_str_or_num + re_tail),
424                 re.compile(re_leading_ws + r'farpush ax' + re_tail),
425                 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
426                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
427                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
428                 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
429         ], farcall1_matchfn, output_fn)
431         all_matchers['objcall0_matcher'] = MultiLineMatcher([
432                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
433                 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
434                 re.compile(re_leading_ws + r'push op' + re_tail),
435                 re.compile(re_leading_ws + r'callobj ax' + re_tail),
436                 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
437                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
438                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
439                 re.compile(re_leading_ws + r'pop op' + re_tail),
440         ], objcall0_matchfn, output_fn)
442         all_matchers['objcall1_matcher'] = MultiLineMatcher([
443                 re.compile(re_leading_ws + r'li bx, ' + re_iden_or_num + re_tail),
444                 re.compile(re_leading_ws + r'li mar, ' + re_iden_or_num + re_tail),
445                 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
446                 re.compile(re_leading_ws + r'push op' + re_tail),
447                 re.compile(re_leading_ws + r'callobj ax' + re_tail),
448                 re.compile(re_leading_ws + r'farpush bx' + re_tail),
449                 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
450                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
451                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
452                 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
453                 re.compile(re_leading_ws + r'pop op' + re_tail),
454         ], objcall1_matchfn, output_fn)
456         all_matchers['objcall1dynstr_matcher'] = MultiLineMatcher([
457                 re.compile(re_leading_ws + r'li ax, ' + re_string + re_tail),
458                 re.compile(re_leading_ws + r'newstr ax' + re_tail),
459                 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
460                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
461                 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
462                 re.compile(re_leading_ws + r'push op' + re_tail),
463                 re.compile(re_leading_ws + r'callobj ax' + re_tail),
464                 re.compile(re_leading_ws + r'farpush bx' + re_tail),
465                 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
466                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
467                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
468                 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
469                 re.compile(re_leading_ws + r'pop op' + re_tail),
470         ], objcall1dynstr_matchfn, output_fn)
472         all_matchers['varislit_matcher'] = MultiLineMatcher([
473                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
474                 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
475                 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
476                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
477                 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
478         ], varislit_matchfn, output_fn)
480         all_matchers['varislit2_matcher'] = MultiLineMatcher([
481                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
482                 re.compile(re_leading_ws + r'memread4 bx' + re_tail),
483                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
484                 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
485         ], varislit2_matchfn, output_fn)
487         all_matchers['incvar_matcher'] = MultiLineMatcher([
488                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
489                 re.compile(re_leading_ws + r'push ax' + re_tail),
490                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
491                 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
492                 re.compile(re_leading_ws + r'pop bx' + re_tail),
493                 re.compile(re_leading_ws + r'(sub|add) ax, bx' + re_tail),
494                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
495                 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
496         ], incvar_matchfn, output_fn)
498         all_matchers['incvar2_matcher'] = MultiLineMatcher([
499                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
500                 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
501                 re.compile(re_leading_ws + r'(subi|addi) ax, ' + re_number + re_tail),
502                 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
503         ], incvar2_matchfn, output_fn)
505         all_matchers['setvar_matcher'] = MultiLineMatcher([
506                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
507                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
508                 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
509         ], setvar_matchfn, output_fn)
511         for i in all_matchers.keys():
512                 matcher_names[all_matchers[i]] = i
514 commandline_args_matcher_map =  {
515         "-cmp" : 'cmp_mr_matcher',
516         "-pushpop": 'pushpop_matcher',
517         "-sourceline": 'sourceline_matcher',
518         "-lnl": 'load_negative_literal_matcher',
519         "-lnl2": 'load_negative_literal2_matcher',
520         "-ll": 'load_literal_matcher',
521         "-cmp2": 'cmp2_matcher',
522         "-axmar": 'axmar_matcher',
523         "-mrswap": 'mr_swap_matcher',
524         "-m4s": 'memread4_swap_matcher',
525         "-ptrstack2x": 'ptrstack2x_matcher',
526         "-rlarith": 'regload_arith_matcher',
527         "-assertlte": "assertlte_matcher",
528         "-load0": "load0_matcher",
529         "-fcdebug": "thisaddr_sourceline_matcher",
530         "-objcall0": "objcall0_matcher",
531         "-objcall1": "objcall1_matcher",
532         "-objcall1dynstr": "objcall1dynstr_matcher",
533         "-farcall0": "farcall0_matcher",
534         "-farcall1": "farcall1_matcher",
535         "-varislit": "varislit_matcher",
536         "-varislit2": "varislit2_matcher",
537         "-incvar": "incvar_matcher",
538         "-incvar2": "incvar2_matcher",
539         "-setvar": "setvar_matcher",
542 help_text = {
543         '-cmp': "optimize cmp/mr",
544         '-cmp2': "optimize gt/gte/lt/lte (requires prev -ll pass)",
545         "-pushpop": "optimize push/pop",
546         "-sourceline": "remove sourceline statements",
547         "-lnl": "optimize negative literal loads",
548         "-lnl2": "optimize negative literal loads variant2",
549         "-ll" : "optimize literal loads",
550         "-axmar": "...",
551         "-mrswap": "remove gratuitous reverse register copy",
552         "-m4s": "optimize register swap after memread",
553         "-ptrstack2x": "optimize duplicate ptrstack statements",
554         "-rlarith": "remove temporary register loads for add/sub",
555         "-assertlte": "remove assertlte statements",
556         "-load0": "replace load of 0 with xor reg, reg",
557         "-fcdebug": "insert sourceline directives on the start of each func (4 debugging)",
558         "-objcall0": "replace objcall0 with macro",
559         "-objcall1": "replace objcall1 with macro",
560         "-objcall1dynstr": "replace objcall1 with macro",
561         "-farcall0": "replace farcall0 with macro",
562         "-farcall1": "replace farcall1 with macro",
563         "-varislit": "varislit macro (neg)",
564         "-varislit2": "varislit macro",
565         "-incvar": "incvar macro",
566         "-incvar2": "incvar macro",
567         "-setvar": "setvar macro",
570 def main():
571         matcher_names = []
572         if len(sys.argv) < 4: return usage()
573         # -cmp -pushpop -sourceline -lnl -ll -cmp2 -axmar -mrswap
574         for i in xrange(1, len(sys.argv)-2):
575                 if not sys.argv[i] in commandline_args_matcher_map: return usage()
576                 else: matcher_names.append(commandline_args_matcher_map[sys.argv[i]])
578         fn = sys.argv[len(sys.argv)-2]
579         fin = open(fn, "r")
580         fout = open(sys.argv[len(sys.argv)-1], "w")
582         prep_matchers()
583         import tempfile
584         for mn in matcher_names:
585                 tmp = tempfile.TemporaryFile()
586                 m = all_matchers[mn]
587                 m.fout = tmp
588                 seek_text(fin, m.fout)
589                 optimize(m, fin)
590                 tmp.seek(0)
591                 fin = tmp
592         while 1:
593                 chunk = fin.read(4096)
594                 if chunk == '': break
595                 fout.write(chunk)
597         s = ''
598         total_removed = 0
599         for i in removed_per_matcher.keys():
600                 if removed_per_matcher[i] == 0: continue
601                 total_removed += removed_per_matcher[i]
602                 s += "[%s:%d] " %(i[:len(i)-len("_matcher")], removed_per_matcher[i])
603         if total_removed:
604                 sys.stdout.write( "%s: removed %d lines %s\n"%(fn, total_removed, s))
605         return 0
608 if __name__ == "__main__": sys.exit(main())