DataFile: add API to access spriteflags count/offset
[rofl0r-agsutils.git] / agsoptimize
blob9701421f869e88ea52cdf77e7be646be1ddedf91
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]))
274 def get_matcher_name(matcher):
275         return matcher_names[matcher]
277 removed_per_matcher = {}
278 def optimize(matcher, fin):
279         global lineno
280         global removed
281         removed = 0
282         while True:
283                 lineno = lineno + 1
284                 s = fin.readline()
285                 if s == '': break
286                 matcher.feed(s)
287         mn = get_matcher_name(matcher)
288         global removed_per_matcher
289         removed_per_matcher[mn] = removed
291 def seek_text(fin, fout):
292         global lineno
293         while True:
294                 lineno = lineno + 1
295                 s = fin.readline()
296                 if s == '': break
297                 fout.write(s)
298                 if s.startswith('.text'): break
300 all_matchers = {}
301 matcher_names = {}
303 re_tail = r"(| \\)$"
304 re_digits = r"([0-9]+)"
305 re_number__ = r"-{0,1}[0-9]+"
306 re_number = r"(" + re_number__ + ")"
307 re_string__ = r'\"[^"]*\"'
308 re_string = r'(' + re_string__ + ')'
309 # variable prefixed with @ means: variable exported by curr script
310 re_var__ = r"@{0,1}[_9A-Za-z]+[0-9A-Za-z_]*"
311 re_var = r"(" + re_var__ + ")"
312 re_var_str_or_num = "(" + "(?:" + re_var__ + ")|(?:" + re_string__ + ")|(?:" + re_number__ +")"  + ")"
313 re_iden_or_num = r"([0-9A-Za-z_]+)"
314 re_leading_ws = r"(\s*)"
315 re_gpr = r"([a-d]x)"
316 re_extfunc = r"([_9A-Za-z]+[0-9A-Za-z_:]*)"
318 def prep_matchers():
319         global all_matchers
320         global matcher_names
321         all_matchers['pushpop_matcher'] = MultiLineMatcher([
322                 re.compile('(\s+)push ([a-z]+)'),
323                 re.compile('(\s+)pop ([a-z]+)'),
324         ], push_pop_matchfn, output_fn)
326         all_matchers['sourceline_matcher'] = MultiLineMatcher([
327                 re.compile('(\s+)sourceline ([0-9]+)'),
328         ], sourceline_matchfn, output_fn)
330         # this matcher is only for debug purposes.
331         # it adds a sourceline statement after every thisaddr,
332         # which is right at the beginning of each func.
333         # so one can set e.g. a breakpoint in the ags interpreter.
334         all_matchers['thisaddr_sourceline_matcher'] = MultiLineMatcher([
335                 re.compile('(\s+)thisaddr ([0-9]+)$'),
336         ], thisaddr_sourceline_matchfn, output_fn)
338         all_matchers['cmp_mr_matcher'] = MultiLineMatcher([
339                 re.compile('(\s+)(cmpeq|cmpne|lor|land) bx, ax'),
340                 re.compile('(\s+)mr ax, bx'),
341         ], cmp_mr_matchfn, output_fn)
343         all_matchers['load_negative_literal_matcher'] = MultiLineMatcher([
344                 re.compile('(\s+)push ax$'),
345                 re.compile('(\s+)li ax, ([0-9]+)$'),
346                 re.compile('(\s+)li bx, 0$'),
347                 re.compile('(\s+)sub bx, ax$'),
348                 re.compile('(\s+)mr ax, bx$'),
349                 re.compile('(\s+)pop bx$'),
350         ], load_negative_literal_matchfn, output_fn)
352         all_matchers['load_negative_literal2_matcher'] = MultiLineMatcher([
353                 re.compile('(\s+)li ax, ([0-9]+)$'),
354                 re.compile('(\s+)li bx, 0$'),
355                 re.compile('(\s+)sub bx, ax$'),
356                 re.compile('(\s+)mr ax, bx$'),
357         ], load_negative_literal2_matchfn, output_fn)
359         all_matchers['load_literal_matcher'] = MultiLineMatcher([
360                 re.compile('(\s+)push ax$'),
361                 re.compile('(\s+)li ax, ([0-9]+)$'),
362                 re.compile('(\s+)pop bx$'),
363         ], load_literal_matchfn, output_fn)
365         all_matchers['cmp2_matcher'] = MultiLineMatcher([
366                 re.compile('(\s+)mr bx, ax'),
367                 re.compile('(\s+)li ax, ([0-9]+)'),
368                 re.compile('(\s+)(gt|gte|lt|lte) bx, ax'),
369                 re.compile('(\s+)mr ax, bx'),
370         ], cmp2_matchfn, output_fn)
372         all_matchers['axmar_matcher'] = MultiLineMatcher([
373                 re.compile('(\s+)li ax, ([0-9]+)$'),
374                 re.compile('(\s+)mr bx, ax$'),
375                 re.compile('(\s+)li mar, (.+)$'),
376                 re.compile('(\s+)mr ax, mar$'),
377         ], axmar_matchfn, output_fn)
379         all_matchers['mr_swap_matcher'] = MultiLineMatcher([
380                 re.compile('(\s+)mr ax, bx'),
381                 re.compile('(\s+)mr bx, ax'),
382         ], mr_swap_matchfn, output_fn)
384         all_matchers['memread4_swap_matcher'] = MultiLineMatcher([
385         # memread4 ax; mr bx, ax; li ax, 1
386                 re.compile('(\s+)memread4 ([a-d]x)'),
387                 re.compile('(\s+)mr ([a-d]x), ([a-d]x)'),
388                 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
389         ], memread4_swap_matchfn, output_fn)
391         all_matchers['ptrstack2x_matcher'] = MultiLineMatcher([
392                 re.compile('(\s+)ptrstack ([0-9]+)$'),
393                 re.compile('(\s+)mem.*$'),
394                 re.compile('(\s+)ptrstack ([0-9]+)$'),
395         ], ptrstack2x_matchfn, output_fn)
397         all_matchers['regload_arith_matcher'] = MultiLineMatcher([
398         # li ax, 1; add bx, ax, ax; mr ax, bx
399                 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
400                 re.compile('(\s+)(add|sub) ([a-d]x), ([a-d]x)$'),
401                 re.compile('(\s+)mr ([a-d]x), ([a-d]x)$'),
402         ], regload_arith_matchfn, output_fn)
404         all_matchers['assertlte_matcher'] = MultiLineMatcher([
405                 re.compile('(\s+)assertlte\s.*'),
406         ], sourceline_matchfn, output_fn)
408         all_matchers['load0_matcher'] = MultiLineMatcher([
409                 re.compile('(\s+)li ([a-d]x), 0$'),
410         ], load0_matchfn, output_fn)
412         # macros. we replace specific snippets with macros, to make the code
413         # more readable.
415         all_matchers['farcall0_matcher'] = MultiLineMatcher([
416                 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
417                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
418                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
419         ], farcall0_matchfn, output_fn)
421         all_matchers['farcall1_matcher'] = MultiLineMatcher([
422                 re.compile(re_leading_ws + r'li ax, ' + re_var_str_or_num + re_tail),
423                 re.compile(re_leading_ws + r'farpush ax' + re_tail),
424                 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
425                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
426                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
427                 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
428         ], farcall1_matchfn, output_fn)
430         all_matchers['objcall0_matcher'] = MultiLineMatcher([
431                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
432                 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
433                 re.compile(re_leading_ws + r'push op' + re_tail),
434                 re.compile(re_leading_ws + r'callobj ax' + re_tail),
435                 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
436                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
437                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
438                 re.compile(re_leading_ws + r'pop op' + re_tail),
439         ], objcall0_matchfn, output_fn)
441         all_matchers['objcall1_matcher'] = MultiLineMatcher([
442                 re.compile(re_leading_ws + r'li bx, ' + re_iden_or_num + re_tail),
443                 re.compile(re_leading_ws + r'li mar, ' + re_iden_or_num + re_tail),
444                 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
445                 re.compile(re_leading_ws + r'push op' + re_tail),
446                 re.compile(re_leading_ws + r'callobj ax' + re_tail),
447                 re.compile(re_leading_ws + r'farpush bx' + re_tail),
448                 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
449                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
450                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
451                 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
452                 re.compile(re_leading_ws + r'pop op' + re_tail),
453         ], objcall1_matchfn, output_fn)
455         all_matchers['objcall1dynstr_matcher'] = MultiLineMatcher([
456                 re.compile(re_leading_ws + r'li ax, ' + re_string + re_tail),
457                 re.compile(re_leading_ws + r'newstr ax' + re_tail),
458                 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
459                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
460                 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
461                 re.compile(re_leading_ws + r'push op' + re_tail),
462                 re.compile(re_leading_ws + r'callobj ax' + re_tail),
463                 re.compile(re_leading_ws + r'farpush bx' + re_tail),
464                 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
465                 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
466                 re.compile(re_leading_ws + r'farcall ax' + re_tail),
467                 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
468                 re.compile(re_leading_ws + r'pop op' + re_tail),
469         ], objcall1dynstr_matchfn, output_fn)
471         all_matchers['varislit_matcher'] = MultiLineMatcher([
472                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
473                 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
474                 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
475                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
476                 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
477         ], varislit_matchfn, output_fn)
479         all_matchers['varislit2_matcher'] = MultiLineMatcher([
480                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
481                 re.compile(re_leading_ws + r'memread4 bx' + re_tail),
482                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
483                 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
484         ], varislit2_matchfn, output_fn)
486         all_matchers['incvar_matcher'] = MultiLineMatcher([
487                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
488                 re.compile(re_leading_ws + r'push ax' + re_tail),
489                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
490                 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
491                 re.compile(re_leading_ws + r'pop bx' + re_tail),
492                 re.compile(re_leading_ws + r'(sub|add) ax, bx' + re_tail),
493                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
494                 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
495         ], incvar_matchfn, output_fn)
497         all_matchers['incvar2_matcher'] = MultiLineMatcher([
498                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
499                 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
500                 re.compile(re_leading_ws + r'(subi|addi) ax, ' + re_number + re_tail),
501                 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
502         ], incvar2_matchfn, output_fn)
504         all_matchers['setvar_matcher'] = MultiLineMatcher([
505                 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
506                 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
507                 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
508         ], setvar_matchfn, output_fn)
510         for i in all_matchers.keys():
511                 matcher_names[all_matchers[i]] = i
513 commandline_args_matcher_map =  {
514         "-cmp" : 'cmp_mr_matcher',
515         "-pushpop": 'pushpop_matcher',
516         "-sourceline": 'sourceline_matcher',
517         "-lnl": 'load_negative_literal_matcher',
518         "-lnl2": 'load_negative_literal2_matcher',
519         "-ll": 'load_literal_matcher',
520         "-cmp2": 'cmp2_matcher',
521         "-axmar": 'axmar_matcher',
522         "-mrswap": 'mr_swap_matcher',
523         "-m4s": 'memread4_swap_matcher',
524         "-ptrstack2x": 'ptrstack2x_matcher',
525         "-rlarith": 'regload_arith_matcher',
526         "-assertlte": "assertlte_matcher",
527         "-load0": "load0_matcher",
528         "-fcdebug": "thisaddr_sourceline_matcher",
529         "-objcall0": "objcall0_matcher",
530         "-objcall1": "objcall1_matcher",
531         "-objcall1dynstr": "objcall1dynstr_matcher",
532         "-farcall0": "farcall0_matcher",
533         "-farcall1": "farcall1_matcher",
534         "-varislit": "varislit_matcher",
535         "-varislit2": "varislit2_matcher",
536         "-incvar": "incvar_matcher",
537         "-incvar2": "incvar2_matcher",
538         "-setvar": "setvar_matcher",
541 help_text = {
542         '-cmp': "optimize cmp/mr",
543         '-cmp2': "optimize gt/gte/lt/lte (requires prev -ll pass)",
544         "-pushpop": "optimize push/pop",
545         "-sourceline": "remove sourceline statements",
546         "-lnl": "optimize negative literal loads",
547         "-lnl2": "optimize negative literal loads variant2",
548         "-ll" : "optimize literal loads",
549         "-axmar": "...",
550         "-mrswap": "remove gratuitous reverse register copy",
551         "-m4s": "optimize register swap after memread",
552         "-ptrstack2x": "optimize duplicate ptrstack statements",
553         "-rlarith": "remove temporary register loads for add/sub",
554         "-assertlte": "remove assertlte statements",
555         "-load0": "replace load of 0 with xor reg, reg",
556         "-fcdebug": "insert sourceline directives on the start of each func (4 debugging)",
557         "-objcall0": "replace objcall0 with macro",
558         "-objcall1": "replace objcall1 with macro",
559         "-objcall1dynstr": "replace objcall1 with macro",
560         "-farcall0": "replace farcall0 with macro",
561         "-farcall1": "replace farcall1 with macro",
562         "-varislit": "varislit macro (neg)",
563         "-varislit2": "varislit macro",
564         "-incvar": "incvar macro",
565         "-incvar2": "incvar macro",
566         "-setvar": "setvar macro",
569 def main():
570         matcher_names = []
571         if len(sys.argv) < 4: return usage()
572         # -cmp -pushpop -sourceline -lnl -ll -cmp2 -axmar -mrswap
573         for i in xrange(1, len(sys.argv)-2):
574                 if not sys.argv[i] in commandline_args_matcher_map: return usage()
575                 else: matcher_names.append(commandline_args_matcher_map[sys.argv[i]])
577         fn = sys.argv[len(sys.argv)-2]
578         fin = open(fn, "r")
579         fout = open(sys.argv[len(sys.argv)-1], "w")
581         prep_matchers()
582         import tempfile
583         for mn in matcher_names:
584                 tmp = tempfile.TemporaryFile()
585                 m = all_matchers[mn]
586                 m.fout = tmp
587                 seek_text(fin, m.fout)
588                 optimize(m, fin)
589                 tmp.seek(0)
590                 fin = tmp
591         while 1:
592                 chunk = fin.read(4096)
593                 if chunk == '': break
594                 fout.write(chunk)
596         s = ''
597         total_removed = 0
598         for i in removed_per_matcher.keys():
599                 if removed_per_matcher[i] == 0: continue
600                 total_removed += removed_per_matcher[i]
601                 s += "[%s:%d] " %(i[:len(i)-len("_matcher")], removed_per_matcher[i])
602         if total_removed:
603                 sys.stdout.write( "%s: removed %d lines %s\n"%(fn, total_removed, s))
606 if __name__ == "__main__": main()