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
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.
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 ***** */
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
46 avmshell peephole.abc -- peephole.tbl peephole.cpp
49 /* Generate peephole optimization state tables from a description.
52 peep ::= pattern guard? action
53 pattern ::= "pattern" ident (";" ident)* eol
55 action ::= expr (";" expr)
57 token ::= any C++ token valid in an expression
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
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
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.
118 function Pattern
(P
, G
, A
) {
124 public function toString
() {
132 const patterns
= new Vector
.<Pattern
>;
133 const children
= new Vector
.<Node
>;
135 function Node
(instr
) {
139 function getChild
(instr
) {
140 for ( var i
=0, limit
=children
.length
; i
< limit
; i
++ )
141 if (children
[i
].instr
== instr
)
143 var c
= new Node
(instr
);
148 public function toString
() {
150 if (patterns
.length
> 0)
151 s
+= " (" + instr
+ " -> {" + patterns
.join
(",") + "})";
152 if (children
.length
> 0)
153 s
+= " (" + instr
+ " -> [" + children
.join
(",") + "])";
161 var numTransitions
= 0;
162 var transitionPtr
= 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.
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
++ )
186 function assert
(cond
) {
188 throw new Error("Assertion failed");
193 function readOpcodes
() {
196 File.read
("wopcodes.cpp").
198 filter
(function (l
) {
200 if (state
== 0 && l
.match
(/^\s
*\/\/\s
*BEGIN
\s
*$/))
202 else if (state
== 1 && l
.match
(/^\s
*\/\/\s
*END
\s
*$/))
204 if (r
&& l
.match
(/^\s
*\/\//))
206 if (r
&& l
.match
(/^\s
*$/))
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
));
216 if (!(r
[3].match
(/^0x
/))) {
219 longest
= Math.max
(longest
, r
[3].length
);
220 if (parseInt
(r
[2]) != 0)
221 jump_opcodes
[r
[3]] = true;
227 function preprocess
(lines
) {
230 for ( var i
=0, limit
=lines
.length
; i
< limit
; i
++ ) {
231 if (/^\s
*$/.test
(lines
[i
]))
233 if (/^\s
*\/\//.test(lines[i]))
235 assert
(/^(\*|pattern
\s
+|guard
\s
+|action
\s
+)/.test
(lines
[i
]));
236 if (lines
[i
].charAt
(0) == '*') {
238 lines
[j
] += lines
[i
].substring
(1);
241 lines
[j
++] = lines
[i
];
248 function parse
(lines
) {
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]");
257 res
= /^pattern
\s
+(.*)$/.exec
(L
);
259 P
= res
[1].split
(/\s
*;\s*/
);
264 res
= /^guard
\s
+(.*)$/.exec
(L
);
272 res
= /^action
\s
+(.*)$/.exec
(L
);
274 A
= res
[1].split
(/\s
*;\s*/
);
277 assert
( P
!= null && A
!= null );
278 output
.push
(new Pattern
(P
, G
, A
));
284 function buildTrie
(patterns
) {
285 var T
= new Node
("");
287 for ( var i
=0, ilimit
=patterns
.length
; i
< ilimit
; i
++ ) {
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
);
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
)) {
309 for ( var y
in opcode
)
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) {
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)
324 for ( q
=s
.ancestor
; q
!= t
&& q
!= null ; q
=q
.ancestor
)
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
++ )
342 for ( var s
=depths
[1] ; s
!= null ; s
=s
.next
)
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
);
350 var s
= f
[sd
.stateno
];
351 while (g
(s
, a
) == -1)
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
) {
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];
377 function expand
(node
, depth
, ancestor
, documentation
) {
378 var state
= new State
;
379 state
.next
= depths
[depth
];
380 depths
[depth
] = state
;
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
);
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
))
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
;
409 function formatStates
() {
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
++ ) {
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);
422 S
.numTransitions
+ ", " +
424 S
.transitionPtr
+ ", " +
425 S
.guardIndex
+ ", " +
427 "// " + ((i
+1)%10 == 0 ? pad
(i
+1,4) : " ") + S
.documentation
);
433 function formatTransitions
() {
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
: ""));
451 function formatToplevel
() {
453 s
.push
("const uint16 WordcodeEmitter::toplevel[] = {");
455 while (i
< MAXINSTR
) {
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
);
470 function formatActions
() {
472 function makeAssert
(P
) {
474 for ( var i
=0 ; i
< P
.length
; i
++ ) {
477 s
+= "I[" + i
+ "][0] == NEW_OPCODE(WOP_" + P
[i
] + ")";
479 return " AvmAssert(" + s
+ ");";
485 s
.push
(" if (" + (A
.G
? A
.G
: "true") + ") {");
486 if (A
.P
[A
.P
.length
-1] in jump_opcodes
) {
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" : "") + ");");
500 s
.push
("bool WordcodeEmitter::commit(uint32 action)");
502 s
.push
(" switch (action) {");
503 for ( var i
=0 ; i
< actions
.length
; 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
++ )
512 s
.push
(makeAssert
(A
.P
));
515 s
.push
(" return false;");
518 s
.push
(" AvmAssert(!\"Should not happen\");");
519 s
.push
(" return false;");
525 if (System.argv
.length
!= 2) {
526 print
("Usage: peephole.abc -- inputfile outputfile");
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" +
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" +
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" +
546 " * The Original Code is [Open Source Virtual Machine.].\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" +
553 " * Contributor(s):\n" +
554 " * Adobe AS3 Team\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" +
568 " * ***** END LICENSE BLOCK ***** */\n\n" +
569 "// Generated by utils/peephole.as\n\n" +
570 "#include \"avmplus.h\"\n\n" +
571 "namespace avmplus\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" +