chris
[tamarin-stm.git] / utils / peephole.as
blob50002e910c3532b25e27e2d1051222db6b8aa4a2
1 /* -*- mode: java -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is [Open Source Virtual Machine.].
17 * The Initial Developer of the Original Code is
18 * Adobe System Incorporated.
19 * Portions created by the Initial Developer are Copyright (C) 2008
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Adobe AS3 Team
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 /* Usage:
41 Run this from the core/ directory, the program needs to know where
42 to find wopcodes.cpp and there's no parameter to specify it.
43 Normally you want the input to be "peephole.tbl" and the output to
44 be "peephole.cpp":
46 avmshell peephole.abc -- peephole.tbl peephole.cpp
49 /* Generate peephole optimization state tables from a description.
51 peepspec ::= peep*
52 peep ::= pattern guard? action
53 pattern ::= "pattern" ident (";" ident)* eol
54 guard ::= expr
55 action ::= expr (";" expr)
56 expr ::= token+
57 token ::= any C++ token valid in an expression
58 | $integer.integer
59 ident ::= C++ identifier
60 eol ::= newline or end-of-file
62 Identifiers in the pattern must be opcode names; these are resolved at
63 compile time and the peephole optimizer state machine uses them to
64 transition from one state to another. The guard is evaluated in case
65 there are side conditions apart from the opcode values; the guard must
66 be a C++ expression. The action represents individual instruction
67 words that replace the instructions matched by the pattern; each word
68 should be a C++ expression.
70 The syntax $n.m refers to the m'th word of the n'th matched instruction.
71 It can be used in the guard and action.
73 The guard will be evaluated zero or one times per match. (The current
74 implementation only invokes a guard after longer matches have failed,
75 so zero times is typical - you can't track state machine progress by
76 having side effects in the guard.)
78 For example (this one has only one instruction in the action):
80 pattern OP_getlocal ; OP_getlocal ; OP_getlocal
81 guard $0.1 < 1024 && $1.1 < 1024 && $2.1 < 1024
82 action OP_get3locals ; ($2.1 << 20) | ($1.1 << 10) | $0.1
84 Continuation lines are allowed, they start with '*' in the first column.
85 The continuation line, less its '*', is pasted onto the end of the
86 preceding line.
88 Comment lines start with // (possibly preceded by blanks). Comment lines
89 and blank lines are ignored everywhere before continuation lines are
90 resolved. Comments can't follow patterns, guards, or actions on the
91 same line.
93 There can be multiple patterns that match the same instruction
94 sequence. The guards are evaluated in the order they appear in the
95 input file, and the first action whose guard is true is selected;
96 the other actions are ignored.
98 Restrictions (not checked by this program):
100 - The action cannot introduce more words than the instruction originally
101 occupied (it might not be hard to lift this restriction).
103 - Lookupswitch must not appear in these patterns.
105 - Any PC-relative branch instruction must be the last in the pattern or
106 in the replacement code.
110 package peephole
112 import avmplus.*;
114 class Pattern
116 const P, G, A;
118 function Pattern(P, G, A) {
119 this.P = P;
120 this.G = G;
121 this.A = A;
124 public function toString() {
125 return G + " " + A;
129 class Node
131 const instr;
132 const patterns = new Vector.<Pattern>;
133 const children = new Vector.<Node>;
135 function Node(instr) {
136 this.instr = instr;
139 function getChild(instr) {
140 for ( var i=0, limit=children.length ; i < limit ; i++ )
141 if (children[i].instr == instr)
142 return children[i];
143 var c = new Node(instr);
144 children.push(c);
145 return c;
148 public function toString() {
149 var s = "";
150 if (patterns.length > 0)
151 s += " (" + instr + " -> {" + patterns.join(",") + "})";
152 if (children.length > 0)
153 s += " (" + instr + " -> [" + children.join(",") + "])";
154 return s;
158 class State
160 var stateno = 0;
161 var numTransitions = 0;
162 var transitionPtr = 0;
163 var guardIndex = 0;
164 var fail = 0;
165 var failShift = 0;
166 var next = null; // next node at same depth as this node, see computeFailures
167 var depth = 0; // depth of this node
168 var ancestor = null; // the node we came from, or null for the start state
169 var documentation = "";
171 public function toString() {
172 return "[" + [stateno,numTransitions,transitionPtr,guardIndex,fail,failShift,depth].join(",") + "]";
176 // readOpcodes() fleshes out these tables.
178 const opcode = {};
179 const jump_opcodes = {};
180 const MAXINSTR = 350; // last opcode in table above plus one would be adequate
182 const opname = new Vector.<String>(MAXINSTR, true);
183 for ( var i=0 ; i < opname.length ; i++ )
184 opname[i] = "-";
186 function assert(cond) {
187 if (!cond)
188 throw new Error("Assertion failed");
191 var longest = 0;
193 function readOpcodes() {
194 var state = 0;
195 var i = 0;
196 File.read("wopcodes.cpp").
197 split("\n").
198 filter(function (l) {
199 var r = state == 1;
200 if (state == 0 && l.match(/^\s*\/\/\s*BEGIN\s*$/))
201 state = 1;
202 else if (state == 1 && l.match(/^\s*\/\/\s*END\s*$/))
203 state = 2;
204 if (r && l.match(/^\s*\/\//))
205 r = false;
206 if (r && l.match(/^\s*$/))
207 r = false;
208 return r;
210 forEach(function (l) {
211 // The format of a line is { num, ... N("name") }
212 // We only care about the second number (the "jumps" attribute) and the
213 // name for the time being.
214 var r = (/^\s*\{\s*([0-9]+)\s*,\s*([0-9]+)[^N]+N\(\"([^\"]+)\"\)/.exec(l));
215 assert(r != null);
216 if (!(r[3].match(/^0x/))) {
217 opcode[r[3]] = i;
218 opname[i] = r[3];
219 longest = Math.max(longest, r[3].length);
220 if (parseInt(r[2]) != 0)
221 jump_opcodes[r[3]] = true;
223 i++;
227 function preprocess(lines) {
228 var j = 0;
229 var first = true;
230 for ( var i=0, limit=lines.length ; i < limit ; i++ ) {
231 if (/^\s*$/.test(lines[i]))
232 continue;
233 if (/^\s*\/\//.test(lines[i]))
234 continue;
235 assert(/^(\*|pattern\s+|guard\s+|action\s+)/.test(lines[i]));
236 if (lines[i].charAt(0) == '*') {
237 assert(!first);
238 lines[j] += lines[i].substring(1);
239 continue;
241 lines[j++] = lines[i];
242 first = false;
244 lines.length = j;
245 return lines;
248 function parse(lines) {
249 var output = [];
250 var P = null;
251 var G = null;
252 var A = null;
253 var res;
254 for ( var i=0, limit=lines.length ; i < limit ; i++ ) {
255 var L = lines[i].replace(/\$([0-9]+)\.([0-9]+)/g, "I[$1][$2]");
256 if (P == null) {
257 res = /^pattern\s+(.*)$/.exec(L);
258 assert(res != null);
259 P = res[1].split(/\s*;\s*/);
260 continue;
263 if (G == null) {
264 res = /^guard\s+(.*)$/.exec(L);
265 if (res != null) {
266 G = res[1];
267 continue;
271 if (A == null) {
272 res = /^action\s+(.*)$/.exec(L);
273 assert(res != null);
274 A = res[1].split(/\s*;\s*/);
277 assert( P != null && A != null );
278 output.push(new Pattern(P, G, A));
279 P = G = A = null;
281 return output;
284 function buildTrie(patterns) {
285 var T = new Node("");
287 for ( var i=0, ilimit=patterns.length ; i < ilimit ; i++ ) {
288 var p = patterns[i];
289 var container = T;
291 for ( var j=0, jlimit=p.P.length ; j < jlimit-1 ; j++ )
292 container = container.getChild(p.P[j]);
293 container.getChild(p.P[jlimit-1]).patterns.push(p);
295 return T;
298 const states = new Vector.<State>;
299 const transitions = new Vector.<Array>;
300 const actions = new Vector.<* /* (Pattern | Vector.<Pattern>) */>;
301 const toplevel = new Vector.<int>(MAXINSTR, true);
302 const depths = new Vector.<State>(20, true);
304 function generate(trie) {
305 var c = trie.children;
306 for ( var i=0 ; i < c.length ; i++ ) {
307 if (!(c[i].instr in opcode)) {
308 print(c[i].instr);
309 for ( var y in opcode )
310 print(y);
312 assert(c[i].instr in opcode);
313 toplevel[opcode[c[i].instr]] = expand(c[i], 1, null, c[i].instr);
315 var failures = computeFailures();
316 for ( var i=1 ; i < failures.length ; i++ ) {
317 assert(failures[i] >= 0);
318 if (failures[i] > 0) {
319 var s = states[i-1];
320 var t = states[failures[i]-1];
321 // Only record failure transition if target state is not on the path to
322 // this state (because if it is then the backtracking logic takes care of it)
323 var q;
324 for ( q=s.ancestor ; q != t && q != null ; q=q.ancestor )
326 if (q == null) {
327 // record a shift that is the difference in depth of the current node and the target node.
328 s.fail = failures[i];
329 s.failShift = s.depth-t.depth;
335 // Dragon Book 2nd Ed exercises 3.31 and 3.32.
337 function computeFailures() {
338 var f = new Vector.<int>(states.length+1, true);
339 for ( var i=0 ; i < f.length ; i++ )
340 f[i] = -1;
342 for ( var s=depths[1] ; s != null ; s=s.next )
343 f[s.stateno] = 0;
345 for ( var d=1 ; d < depths.length ; d++ ) {
346 for ( var sd=depths[d] ; sd != null ; sd=sd.next ) {
347 for ( var a=0 ; a < MAXINSTR ; a++ ) {
348 var s2 = g(sd.stateno, a);
349 if (s2 != -1) {
350 var s = f[sd.stateno];
351 while (g(s, a) == -1)
352 s = f[s];
353 f[s2] = g(s, a);
358 return f;
360 // g(m,x) = n >= 0 if there is a transition from m to n on x
361 // g(0,x) = 0 if there is no transition from 0 on x
362 // g(m,x) = -1 if m > 0 and there is no transition from m on x
364 function g(stateno, input) {
365 if (stateno == 0)
366 return toplevel[input];
367 var s = states[stateno-1];
368 var t = s.transitionPtr;
369 var n = s.numTransitions;
370 for ( var i=0 ; i < n ; i++ )
371 if (transitions[t+i][0] == input)
372 return transitions[t+i][1];
373 return -1;
377 function expand(node, depth, ancestor, documentation) {
378 var state = new State;
379 state.next = depths[depth];
380 depths[depth] = state;
381 state.depth = depth;
382 state.ancestor = ancestor;
383 state.documentation = documentation;
384 if (node.patterns.length > 0) {
385 state.guardIndex = actions.length + 1;
386 if (node.patterns.length > 1)
387 actions.push(node.patterns);
388 else
389 actions.push(node.patterns[0]);
391 var trans = new Vector.<Array>;
392 for ( var i=0 ; i < node.children.length ; i++ ) {
393 var ci = node.children[i];
394 if (!(ci.instr in opcode))
395 print(ci.instr);
396 assert(ci.instr in opcode);
397 trans.push([opcode[ci.instr], expand(ci, depth+1, state, documentation + " " + ci.instr)]);
399 trans.sort(function (a,b) { return a[0] - b[0] });
400 state.numTransitions = trans.length;
401 state.transitionPtr = trans.length > 0 ? transitions.length : 0;
402 for ( var i=0 ; i < trans.length ; i++ )
403 transitions.push(trans[i]);
404 var stateno = states.push(state);
405 state.stateno = stateno;
406 return stateno;
409 function formatStates() {
410 var s = [];
411 s.push("const WordcodeEmitter::peep_state_t WordcodeEmitter::states[] = {");
412 s.push("//n s t g f");
413 s.push("{ 0, 0, 0, 0, 0 }, // Invalid");
414 for ( var i=0 ; i < states.length ; i++ ) {
415 var S = states[i];
416 assert(S.numTransitions < 256);
417 assert(S.failShift < 256);
418 assert(S.transitionPtr < 65536);
419 assert(S.guardIndex < 65536);
420 assert(S.fail < 65536);
421 s.push( "{ " +
422 S.numTransitions + ", " +
423 S.failShift + ", " +
424 S.transitionPtr + ", " +
425 S.guardIndex + ", " +
426 S.fail +" }, " +
427 "// " + ((i+1)%10 == 0 ? pad(i+1,4) : " ") + S.documentation);
429 s.push("};");
430 return s.join("\n");
433 function formatTransitions() {
434 var s = [];
435 s.push("const WordcodeEmitter::peep_transition_t WordcodeEmitter::transitions[] = {");
436 for ( var i=0 ; i < transitions.length ; i++ ) {
437 assert(transitions[i][1] < 65536);
438 s.push("{ WOP_" + opname[transitions[i][0]] + ", " + transitions[i][1] + " }," + (i > 0 && i % 10 == 0 ? " // " + i : ""));
440 s.push("};");
441 return s.join("\n");
444 function pad(s,n) {
445 s = String(s);
446 while (s.length < n)
447 s += " ";
448 return s;
451 function formatToplevel() {
452 var s = [];
453 s.push("const uint16 WordcodeEmitter::toplevel[] = {");
454 var i=0;
455 while (i < MAXINSTR) {
456 var t = "";
457 var n = "";
458 for ( var j=0 ; j < 8 && i < MAXINSTR ; j++, i++ ) {
459 assert(toplevel[i] < 65536);
460 t += toplevel[i] + ", ";
461 n += " " + pad(opname[i], longest);
463 t += "//" + n;
464 s.push(t);
466 s.push("};");
467 return s.join("\n");
470 function formatActions() {
472 function makeAssert(P) {
473 var s = "";
474 for ( var i=0 ; i < P.length ; i++ ) {
475 if (i > 0)
476 s += " && ";
477 s += "I[" + i + "][0] == NEW_OPCODE(WOP_" + P[i] + ")";
479 return " AvmAssert(" + s + ");";
482 function emit(A) {
483 var s = [];
484 var undo = false;
485 s.push(" if (" + (A.G ? A.G : "true") + ") {");
486 if (A.P[A.P.length-1] in jump_opcodes) {
487 undo = true;
488 s.push(" undoRelativeOffsetInJump();");
490 s.push(" S[0] = WOP_" + A.A[0] + ";");
491 s.push(" R[0] = NEW_OPCODE(WOP_" + A.A[0] + ");");
492 for ( var j=1 ; j < A.A.length ; j++ )
493 s.push(" R[" + j + "] = " + A.A[j] + ";");
494 s.push(" return replace(" + A.P.length + "," + A.A.length + (undo ? ",true" : "") + ");");
495 s.push(" }");
496 return s.join("\n");
499 var s = [];
500 s.push("bool WordcodeEmitter::commit(uint32 action)");
501 s.push("{");
502 s.push(" switch (action) {");
503 for ( var i=0 ; i < actions.length ; i++ ) {
504 var A = actions[i];
505 s.push(" case " + (i+1) + ":");
506 if (A is Vector.<Pattern>) {
507 s.push(makeAssert(A[0].P));
508 for ( var j=0 ; j < A.length ; j++ )
509 s.push(emit(A[j]));
511 else {
512 s.push(makeAssert(A.P));
513 s.push(emit(A));
515 s.push(" return false;");
517 s.push(" default:");
518 s.push(" AvmAssert(!\"Should not happen\");");
519 s.push(" return false;");
520 s.push(" }");
521 s.push("}");
522 return s.join("\n");
525 if (System.argv.length != 2) {
526 print("Usage: peephole.abc -- inputfile outputfile");
527 System.exit(1);
530 readOpcodes();
531 generate(buildTrie(parse(preprocess(File.read(System.argv[0]).split("\n")))));
532 File.write(System.argv[1],
533 "/* ***** BEGIN LICENSE BLOCK *****\n" +
534 " * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n" +
535 " *\n" +
536 " * The contents of this file are subject to the Mozilla Public License Version\n" +
537 " * 1.1 (the \"License\"); you may not use this file except in compliance with\n" +
538 " * the License. You may obtain a copy of the License at\n" +
539 " * http://www.mozilla.org/MPL/\n" +
540 " *\n" +
541 " * Software distributed under the License is distributed on an \"AS IS\" basis,\n" +
542 " * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n" +
543 " * for the specific language governing rights and limitations under the\n" +
544 " * License.\n" +
545 " *\n" +
546 " * The Original Code is [Open Source Virtual Machine.].\n" +
547 " *\n" +
548 " * The Initial Developer of the Original Code is\n" +
549 " * Adobe System Incorporated.\n" +
550 " * Portions created by the Initial Developer are Copyright (C) 2008\n" +
551 " * the Initial Developer. All Rights Reserved.\n" +
552 " *\n" +
553 " * Contributor(s):\n" +
554 " * Adobe AS3 Team\n" +
555 " *\n" +
556 " * Alternatively, the contents of this file may be used under the terms of\n" +
557 " * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n" +
558 " * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n" +
559 " * in which case the provisions of the GPL or the LGPL are applicable instead\n" +
560 " * of those above. If you wish to allow use of your version of this file only\n" +
561 " * under the terms of either the GPL or the LGPL, and not to allow others to\n" +
562 " * use your version of this file under the terms of the MPL, indicate your\n" +
563 " * decision by deleting the provisions above and replace them with the notice\n" +
564 " * and other provisions required by the GPL or the LGPL. If you do not delete\n" +
565 " * the provisions above, a recipient may use your version of this file under\n" +
566 " * the terms of any one of the MPL, the GPL or the LGPL.\n" +
567 " *\n" +
568 " * ***** END LICENSE BLOCK ***** */\n\n" +
569 "// Generated by utils/peephole.as\n\n" +
570 "#include \"avmplus.h\"\n\n" +
571 "namespace avmplus\n" +
572 "{\n" +
573 "#ifdef AVMPLUS_PEEPHOLE_OPTIMIZER\n\n" +
575 formatStates() + "\n\n" +
576 formatTransitions() + "\n\n" +
577 formatToplevel() + "\n\n" +
578 formatActions() + "\n\n" +
580 "#endif // AVMPLUS_PEEPHOLE_OPTIMIZER\n" +
581 "}\n");