1 /* -*- mode: java; tab-width: 4 -*- */
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) 2004-2006
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 ***** */
39 // Rewrites Interpreter.asm (generated by MSVC) to convert it from switch
40 // dispatch to direct threaded dispatch. See comments in core/Interpreter.cpp
41 // for a discussion of why this program exists at all.
43 // Follow these 11 easy steps:
45 // - Select the Release configuration in MSVC
47 // - Check that FastInterpreter.asm is excluded from the build
48 // of the avmplus component
50 // - Check that Interpreter.cpp is included in the build
52 // - Check that assembly listing generation is turned on for avmplus
53 // (Configuration > C/C++ > Output files > Assembler output),
54 // you must select "Assembly with Source Code" (/FAs).
56 // - Build. You will have lots of linking errors and no .exe.
58 // - Now open platform/win32/obj_8/avmplus/Release/Interpreter.asm
59 // in an editor and search for "NEXT;". It will be found
60 // in some comment (it's source code). Above the comment
61 // there is some code that looks mostly like this:
63 // mov eax, DWORD PTR [edi]
65 // cmp eax, 511 ; 000001ffH
66 // mov DWORD PTR _pc$[ebp], edi
69 // Immediately /prior/ to that code is a label. Record it.
71 // - Invoke x86rewrite with three arguments: the label you recorded
72 // in the previous step, the path to the above mentioned Interpreter.asm,
73 // and the path to the file core/FastInterpreter.asm. This program
74 // reads the former and generates a new version of the latter.
76 // - Invoke "Build > Clean"
78 // - Include FastInterpreter.asm in the build
80 // - Exclude Interpreter.cpp from the build
82 // - Build again, it should compile and link.
86 // - Only tested on VS2005 sp 1 "Release" mode output so far.
87 // Obviously you're at the mercy of the optimizer and register
88 // allocator, and there is little guarantee that this program will
89 // in fact remain fully applicable to the output of the compiler.
90 // In fact, there are several hacks in this code to work around
91 // arbitrary problems, usually noted in the comments.
93 // - Sometimes MSVC does not place a label correctly in the compiler
94 // output; I've experienced this with L_ext_push_doublebits. The
95 // reason is that the labels are not referenced from the compiled
96 // code. So there is a 'default' case far down in the interpreter
97 // code that inserts artificial conditional jumps to labels found to
98 // have this problem. Add more as needed.
104 function assert
(cond
) {
106 throw new Error("Assertion failed");
109 // Remove inline functions that are duplicated in the ASM output.
110 // These are always PUBLIC followed by _TEXT SEGMENT .. _TEXT ENDS.
111 // We don't need them because they have been inlined into
112 // the functions we do want to keep. Instead of guessing what to
113 // remove, we have a list of functions to keep.
115 // (Note we have to deal with multiple PUBLIC directives, only some
116 // of which have trailing text segments.)
118 // We need to keep the EXTRN definitions that are inside the
119 // inline functions, though, as those symbols are referenced from
120 // the inlined versions too.
122 // The inclusion of branchCheck in the followin list is a hack
123 // required to get linking to work. Probably brittle.
125 // The hacking up of EXTRN definitions for integer_i and integer_u
126 // are hacks required to get linking to work. Probably brittle.
128 function cleanup
(lines
) {
129 var special
= [/[^a
-zA
-Z0
-9]getTraits
[^a
-zA
-Z0
-9]/,
130 /[^a
-zA
-Z0
-9]initMultiname
[^a
-zA
-Z0
-9]/,
131 /[^a
-zA
-Z0
-9]interpN
[^a
-zA
-Z0
-9]/,
132 /[^a
-zA
-Z0
-9]interp32
[^a
-zA
-Z0
-9]/,
133 /[^a
-zA
-Z0
-9]interp
[^a
-zA
-Z0
-9]/,
134 /[^a
-zA
-Z0
-9]branchCheck
[^a
-zA
-Z0
-9]/];
140 while (i
< lines
.length
) {
141 if (/^PUBLIC
\t/.test
(lines
[i
])) {
142 var probe
= /^PUBLIC
\s
+(\?integer_
(?:i
|u
)@[^\s
]+)/.exec
(lines
[i
]);
144 new_lines
.push
("EXTRN\t" + probe
[1] + ":PROC");
146 for ( var j
=0 ; j
< special
.length
; j
++ ) {
147 if (special
[j
].test
(lines
[i
])) {
152 if (!keep
&& /^PUBLIC
\t/.test
(lines
[i
+1])) {
157 if (i
== lines
.length
)
159 var is_extern
= /^EXTRN
\s
/.test
(lines
[i
]);
160 var is_ends
= /^_TEXT
\tENDS/.test
(lines
[i
]);
161 if (keep
|| is_extern
)
162 new_lines
.push
(lines
[i
]);
169 new_lines
.push
(lines
[i
]);
177 // Construct the jump table by rewriting the code.
179 function jumptable
(lines
) {
181 // find and remove call lines, save label offsets in jump table
182 // find and remove the line that defines the jump table, store in 'definer'
184 var labelcall
= /^\s
+call
\s
+\?LLLLABEL_0x
([0-9A
-Za
-z
]+)_L_
([a
-zA
-Z0
-9_
]+)@/;
189 for ( var i
=0 ; i
< lines
.length
; i
++ ) {
190 var probe
= labelcall
.exec
(lines
[i
]);
192 if (probe
[2] != "illegal_op")
193 calls
["L_" + probe
[2]] = { offset
: parseInt
(probe
[1], 16) };
194 var probe2
= /^(\?opcode_labels
@.+)\s
+DD
\s
+([0-9A
-Fa
-f
]+H
)\s
+DUP
/.exec
(lines
[i
]);
195 if (probe2
!= null) {
197 tablesize
= parseInt
(probe2
[2], 16);
199 var probe3
= /^EXTRN
\s
+\?LLLLABEL_
/.test
(lines
[i
]);
200 if (!(probe
|| probe2
|| probe3
))
201 new_lines
.push
(lines
[i
]);
203 assert
(definer
!= null);
205 // search for the label definitions, store label names with the table offsets
207 var labelsearch
= "";
208 for ( var idx
in calls
) {
209 if (labelsearch
!= "")
213 labelsearch
= new RegExp("^(\\$(L_illegal_op|" + labelsearch
+ ")\\$\\d+):");
215 var illegal_label
= null;
216 for ( var i
=0 ; i
< new_lines
.length
; i
++ ) {
217 var probe
= labelsearch
.exec
(new_lines
[i
]);
219 if (probe
[2] == "L_illegal_op")
220 illegal_label
= probe
[1];
222 calls
[probe
[2]].label
= probe
[1];
225 assert
(illegal_label
!= null);
227 // construct and emit a definition of the jump table, attach it to the end
228 // of the segment for 'interp' (labels appear to be scoped in masm)
232 for ( var idx
in calls
)
233 acalls
[calls
[idx
].offset
] = calls
[idx
].label
;
234 for ( var i
=0 ; i
< tablesize
; i
++ ) {
236 dds
.push
("DD\t" + illegal_label
);
238 dds
.push
("DD\t" + acalls
[i
]);
244 for ( var i
=0 ; i
< lines
.length
; i
++ ) {
245 if (/^PUBLIC
\s
\?interp
@/.test
(lines
[i
]))
247 if (flag
&& /^.+\s
+ENDP
/.test
(lines
[i
])) {
248 new_lines
.push
(definer
+ ":");
249 for ( var j
=0 ; j
< dds
.length
; j
++ )
250 new_lines
.push
(dds
[j
]);
253 new_lines
.push
(lines
[i
]);
259 // The NEXT sequence. MSVC keeps SP in memory, and PC is in EDI on entry
260 // to an instruction but must be saved to memory by the time we leave -
261 // some instructions will use EDI for other purposes on entry without saving
262 // it first, then will reload PC into EDI as needed. But sometimes that
263 // leaves something in EDI that is not the PC when we come to the dispatch.
265 function mkDispatch
(scratchreg
, pcreg
) {
266 return ["\tmov\t" + scratchreg
+ ", DWORD PTR [" + pcreg
+ "]",
267 "\tadd\t" + pcreg
+ ", 4",
268 "\tmov\tDWORD PTR _pc$[ebp], " + pcreg
,
269 "\tjmp\t" + scratchreg
];
272 // Find the jump instructions to replace by NEXT sequences. This
273 // includes the initial dispatch, at the top-of-loop label itself.
275 function patchupNext
(lines
, label
, next
) {
277 var lblmatch
= new RegExp("^\\" + label
+ ":");
278 var jmpmatch
= new RegExp("^\\s+jmp\\s+(?:SHORT\\s+)?\\" + label
);
279 for ( var i
=0 ; i
< lines
.length
; i
++ ) {
280 if (jmpmatch
.test
(lines
[i
])) {
282 new_lines
.push
("; " + lines
[i
]);
284 else if (lblmatch
.test
(lines
[i
])) {
285 new_lines
.push
(lines
[i
]);
289 new_lines
.push
(lines
[i
]);
294 for ( var j
=0 ; j
< next
.length
; j
++ )
295 new_lines
.push
(next
[j
]);
301 if (System.argv
.length
!= 3) {
302 print
("Usage: x86rewrite label inputfile outputfile");
303 print
(" label should be the full label at the switch, eg '$LL417@interp'");
307 File.write
(System.argv
[2],
308 patchupNext
(jumptable
(cleanup
(File.read
(System.argv
[1]).split
("\n"))),
310 mkDispatch
("eax","edi")).join
("\n"));