chris
[tamarin-stm.git] / utils / x86rewrite.as
blobbfdd3db14848ce5838dfc1b95444905eeef1c5eb
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
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) 2004-2006
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 // 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]
64 // add edi, 4
65 // cmp eax, 511 ; 000001ffH
66 // mov DWORD PTR _pc$[ebp], edi
67 // ja $LN497@interp
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.
84 // Caveats:
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.
100 package x86rewrite
102 import avmplus.*;
104 function assert(cond) {
105 if (!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]/];
136 var i = 0;
137 var new_lines = [];
138 var flag = false;
139 outer:
140 while (i < lines.length) {
141 if (/^PUBLIC\t/.test(lines[i])) {
142 var probe = /^PUBLIC\s+(\?integer_(?:i|u)@[^\s]+)/.exec(lines[i]);
143 if (probe != null)
144 new_lines.push("EXTRN\t" + probe[1] + ":PROC");
145 var keep = false;
146 for ( var j=0 ; j < special.length ; j++ ) {
147 if (special[j].test(lines[i])) {
148 keep = true;
149 break;
152 if (!keep && /^PUBLIC\t/.test(lines[i+1])) {
153 i++;
154 continue;
156 for (;;) {
157 if (i == lines.length)
158 break;
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]);
163 i++;
164 if (is_ends)
165 break;
168 else {
169 new_lines.push(lines[i]);
170 i++;
173 return new_lines;
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_]+)@/;
185 var calls = {};
186 var definer = null;
187 var tablesize = 0;
188 var new_lines = [];
189 for ( var i=0 ; i < lines.length ; i++ ) {
190 var probe = labelcall.exec(lines[i]);
191 if (probe != null)
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) {
196 definer = probe2[1];
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 != "")
210 labelsearch += "|";
211 labelsearch += idx;
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]);
218 if (probe != null) {
219 if (probe[2] == "L_illegal_op")
220 illegal_label = probe[1];
221 else
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)
230 var acalls = [];
231 var dds = [];
232 for ( var idx in calls )
233 acalls[calls[idx].offset] = calls[idx].label;
234 for ( var i=0 ; i < tablesize ; i++ ) {
235 if (!(i in acalls))
236 dds.push("DD\t" + illegal_label);
237 else
238 dds.push("DD\t" + acalls[i]);
241 lines = new_lines;
242 new_lines = [];
243 var flag = false;
244 for ( var i=0 ; i < lines.length ; i++ ) {
245 if (/^PUBLIC\s\?interp@/.test(lines[i]))
246 flag = true;
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]);
251 flag = false;
253 new_lines.push(lines[i]);
256 return new_lines;
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) {
276 var new_lines = [];
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])) {
281 add();
282 new_lines.push("; " + lines[i]);
284 else if (lblmatch.test(lines[i])) {
285 new_lines.push(lines[i]);
286 add();
288 else
289 new_lines.push(lines[i]);
291 return new_lines;
293 function add() {
294 for ( var j=0 ; j < next.length ; j++ )
295 new_lines.push(next[j]);
299 // Main
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'");
304 System.exit(1);
307 File.write(System.argv[2],
308 patchupNext(jumptable(cleanup(File.read(System.argv[1]).split("\n"))),
309 System.argv[0],
310 mkDispatch("eax","edi")).join("\n"));