/[jscoverage]/trunk/js/imacro_asm.js.in
ViewVC logotype

Contents of /trunk/js/imacro_asm.js.in

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (12 years, 4 months ago) by siliconforks
File size: 15165 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* vim: set sw=4 ts=8 et tw=78 ft=javascript: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
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/
9 *
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.
14 *
15 * The Original Code is the TraceMonkey IMacro Assembler.
16 *
17 * The Initial Developer of the Original Code is
18 * Brendan Eich <brendan@mozilla.org>.
19 * Portions created by the Initial Developer are Copyright (C) 2008
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38 /*
39 * An imacro (interpreter-macro) assembler in JS, with a light dusting of C
40 * pre-processor. We depend on the snarf function from the js shell, defined
41 * originally for the Narcissus metacircular evaluator, now unconditionally
42 * compiled into the shell.
43 *
44 * Filename suffix conventions, used by Makefile.in rules:
45 * .js.in C-pre-processed JS source
46 * .jsasm SpiderMonkey JS assembly source, which could be input to other
47 * assemblers than imacro_asm.js, hence the generic suffix!
48 * .c.out C source output by imacro_asm.js
49 */
50
51 #define ASSERT(cond) _ASSERT(cond, #cond)
52
53 function _ASSERT(cond, message) {
54 if (!cond)
55 throw new Error("Assertion failed: " + message);
56 }
57
58 const js_arguments_str = "arguments";
59 const js_new_str = "new";
60 const js_typeof_str = "typeof";
61 const js_void_str = "void";
62 const js_null_str = "null";
63 const js_this_str = "this";
64 const js_false_str = "false";
65 const js_true_str = "true";
66 const js_throw_str = "throw";
67 const js_in_str = "in";
68 const js_instanceof_str = "instanceof";
69 const js_getter_str = "getter";
70 const js_setter_str = "setter";
71
72 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
73 {jsop: #op, opcode: val, opname: name, opsrc: token, oplen: length, \
74 pops: nuses, pushes: ndefs, precedence: prec, flags: #format},
75
76 const NULL = null;
77
78 const opinfo = [
79 #include "jsopcode.tbl"
80 ];
81
82 const opname2info = {};
83 const jsop2opcode = {};
84
85 for (let i = 0; i < opinfo.length; i++) {
86 let info = opinfo[i];
87 ASSERT(info.opcode == i);
88 opname2info[info.opname] = info;
89 jsop2opcode[info.jsop] = info.opcode;
90 }
91
92 function format_offset(n, w) {
93 let s = n.toString();
94 while (s.length < w)
95 s = ' ' + s;
96 return s;
97 }
98
99 function immediate(op) {
100 let info = op.info;
101 let imm1Expr = /^\(/.test(op.imm1);
102 if (info.flags.indexOf("JOF_ATOM") >= 0) {
103 if (/^(?:void|object|function|string|number|boolean)$/.test(op.imm1))
104 return "0, COMMON_TYPE_ATOM_INDEX(JSTYPE_" + op.imm1.toUpperCase() + ")";
105 return "0, COMMON_ATOM_INDEX(" + op.imm1 + ")";
106 }
107 if (info.flags.indexOf("JOF_JUMP") >= 0) {
108 ASSERT(!imm1Expr);
109 return ((op.target >> 8) & 0xff) + ", " + (op.target & 0xff);
110 }
111 if (info.flags.indexOf("JOF_UINT8") >= 0 ||
112 info.flags.indexOf("JOF_INT8") >= 0) {
113 if (imm1Expr)
114 return op.imm1;
115 if (isNaN(Number(op.imm1)) || Number(op.imm1) != parseInt(op.imm1))
116 throw new Error("invalid 8-bit operand: " + op.imm1);
117 return (op.imm1 & 0xff);
118 }
119 if (info.flags.indexOf("JOF_UINT16") >= 0) {
120 if (imm1Expr)
121 return '(_ & 0xff00) >> 8, (_ & 0xff)'.replace(/_/g, op.imm1);
122 return ((op.imm1 & 0xff00) >> 8) + ", " + (op.imm1 & 0xff);
123 }
124 throw new Error(info.jsop + " format not yet implemented");
125 }
126
127 function simulate_cfg(imacro, depth, i) {
128 while (i < imacro.code.length) {
129 let op = imacro.code[i];
130 depth -= (op.info.pops < 0) ? 2 + Number(op.imm1) : op.info.pops;
131 depth += op.info.pushes;
132
133 if (imacro.depths.hasOwnProperty(i) && imacro.depths[i] != depth)
134 throw Error("Mismatched depth at " + imacro.filename + ":" + op.line);
135
136 /*
137 * Underflowing depth isn't necessarily fatal; most of the imacros
138 * assume they are called with N>0 args so some assume it's ok to go
139 * to some depth <N. We simulate starting from 0, as we've no idea
140 * what else to do.
141 *
142 * if (depth < 0)
143 * throw Error("Negative static-stack depth at " + imacro.filename + ":" + op.line);
144 */
145 if (depth > imacro.maxdepth)
146 imacro.maxdepth = depth;
147 imacro.depths[i] = depth;
148
149 if (op.hasOwnProperty("target_index")) {
150 if (op.target_index <= i)
151 throw Error("Backward jump at " + imacro.filename + ":" + op.line);
152
153 simulate_cfg(imacro, depth, op.target_index);
154
155 if (op.info.opname == "goto" || op.info.opname == "gotox")
156 return;
157 }
158 ++i;
159 }
160 }
161
162 /*
163 * Syntax (spaces are significant only to delimit tokens):
164 *
165 * Assembly ::= (Directive? '\n')*
166 * Directive ::= (name ':')? Operation
167 * Operation ::= opname Operands?
168 * Operands ::= Operand (',' Operand)*
169 * Operand ::= name | number | '(' Expr ')'
170 * Expr ::= a constant-expression in the C++ language
171 * containing no parentheses
172 *
173 * We simplify given line structure and the maximum of one immediate operand,
174 * by parsing using split and regexps. For ease of parsing, parentheses are
175 * banned in an Expr for now, even in quotes or a C++ comment.
176 *
177 * Pseudo-ops start with . and include .igroup and .imacro, terminated by .end.
178 * .imacro must nest in .igroup, neither nests in itself. See imacros.jsasm for
179 * examples.
180 */
181 const line_regexp_parts = [
182 "^(?:(\\w+):)?", // optional label at start of line
183 "\\s*(\\.?\\w+)", // optional spaces, (pseudo-)opcode
184 "(?:\\s+(\\w+|\\([^)]*\\)))?", // optional first immediate operand
185 "(?:\\s+([\\w-]+|\\([^)]*\\)))?", // optional second immediate operand
186 "(?:\\s*(?:#.*))?$" // optional spaces and comment
187 ];
188
189 const line_regexp = new RegExp(line_regexp_parts.join(""));
190
191 function assemble(filename) {
192 let igroup = null, imacro = null;
193 let opcode2extra = [];
194 let igroups = [];
195
196 print("/* GENERATED BY imacro_asm.js -- DO NOT EDIT!!! */");
197
198 let s = snarf(filename);
199 let a = s.split('\n');
200 for (let i = 0; i < a.length; i++) {
201 if (/^\s*(?:#.*)?$/.test(a[i]))
202 continue;
203 let m = line_regexp.exec(a[i]);
204 if (!m)
205 throw new Error(a[i]);
206
207 let [, label, opname, imm1, imm2] = m;
208
209 if (opname[0] == '.') {
210 if (label)
211 throw new Error("invalid label " + label + " before " + opname);
212
213 switch (opname) {
214 case ".igroup":
215 if (!imm1)
216 throw new Error("missing .igroup name");
217 if (igroup)
218 throw new Error("nested .igroup " + imm1);
219 let oprange = imm2.match(/^(\w+)(?:-(\w+))?$/);
220 if (!oprange)
221 throw new Error("invalid igroup operator range " + imm2);
222 let firstop = jsop2opcode[oprange[1]];
223 igroup = {
224 name: imm1,
225 firstop: firstop,
226 lastop: oprange[2] ? jsop2opcode[oprange[2]] : firstop,
227 imacros: []
228 };
229 break;
230
231 case ".imacro":
232 if (!igroup)
233 throw new Error(".imacro outside of .igroup");
234 if (!imm1)
235 throw new Error("missing .imacro name");
236 if (imacro)
237 throw new Error("nested .imacro " + imm1);
238 imacro = {
239 name: imm1,
240 offset: 0,
241 code: [],
242 labeldefs: {},
243 labeldef_indexes: {},
244 labelrefs: {},
245 filename: filename,
246 depths: {}
247 };
248 break;
249
250 case ".end":
251 if (!imacro) {
252 if (!igroup)
253 throw new Error(".end without prior .igroup or .imacro");
254 if (imm1 && (imm1 != igroup.name || imm2))
255 throw new Error(".igroup/.end name mismatch");
256
257 let maxdepth = 0;
258
259 print("static struct {");
260 for (let j = 0; j < igroup.imacros.length; j++) {
261 imacro = igroup.imacros[j];
262 print(" jsbytecode " + imacro.name + "[" + imacro.offset + "];");
263 }
264 print("} " + igroup.name + "_imacros = {");
265
266 for (let j = 0; j < igroup.imacros.length; j++) {
267 let depth = 0;
268
269 imacro = igroup.imacros[j];
270 print(" {");
271 for (let k = 0; k < imacro.code.length; k++) {
272 let op = imacro.code[k];
273
274 print("/*" + format_offset(op.offset,2) + "*/ " + op.info.jsop +
275 (op.imm1 ? ", " + immediate(op) : "") + ",");
276
277 }
278
279 imacro.maxdepth = 0;
280 simulate_cfg(imacro, 0, 0);
281 if (imacro.maxdepth > maxdepth)
282 maxdepth = imacro.maxdepth;
283
284 print(" },");
285 }
286
287 print("};");
288
289 let opcode = igroup.firstop;
290 let oplast = igroup.lastop;
291 do {
292 opcode2extra[opcode] = maxdepth;
293 } while (opcode++ != oplast);
294 igroups.push(igroup);
295 igroup = null;
296 } else {
297 ASSERT(igroup);
298
299 if (imm1 && imm1 != imacro.name)
300 throw new Error(".imacro/.end name mismatch");
301
302 // Backpatch the forward references to labels that must now be defined.
303 for (label in imacro.labelrefs) {
304 if (!imacro.labelrefs.hasOwnProperty(label))
305 continue;
306 if (!imacro.labeldefs.hasOwnProperty(label))
307 throw new Error("label " + label + " used but not defined");
308 let link = imacro.labelrefs[label];
309 ASSERT(link >= 0);
310 for (;;) {
311 let op = imacro.code[link];
312 ASSERT(op);
313 ASSERT(op.hasOwnProperty('target'));
314 let next = op.target;
315 op.target = imacro.labeldefs[label] - op.offset;
316 op.target_index = imacro.labeldef_indexes[label];
317 if (next < 0)
318 break;
319 link = next;
320 }
321 }
322
323 igroup.imacros.push(imacro);
324 }
325 imacro = null;
326 break;
327
328 default:
329 throw new Error("unknown pseudo-op " + opname);
330 }
331 continue;
332 }
333
334 if (!opname2info.hasOwnProperty(opname))
335 throw new Error("unknown opcode " + opname + (label ? " (label " + label + ")" : ""));
336
337 let info = opname2info[opname];
338 if (info.oplen == -1)
339 throw new Error("unimplemented opcode " + opname);
340
341 if (!imacro)
342 throw new Error("opcode " + opname + " outside of .imacro");
343
344 // Blacklist ops that may or must use an atomized double immediate.
345 switch (info.opname) {
346 case "double":
347 case "lookupswitch":
348 case "lookupswitchx":
349 throw new Error(op.opname + " opcode not yet supported");
350 }
351
352 if (label) {
353 imacro.labeldefs[label] = imacro.offset;
354 imacro.labeldef_indexes[label] = imacro.code.length;
355 }
356
357 let op = {offset: imacro.offset, info: info, imm1: imm1, imm2: imm2, line:(i+1) };
358 if (info.flags.indexOf("JOF_JUMP") >= 0) {
359 if (imacro.labeldefs.hasOwnProperty(imm1)) {
360 // Backward reference can be resolved right away, no backpatching needed.
361 op.target = imacro.labeldefs[imm1] - op.offset;
362 op.target_index = imacro.labeldef_indexes[imm1];
363 } else {
364 // Link op into the .target-linked backpatch chain at labelrefs[imm1].
365 // The linked list terminates with a -1 sentinel.
366 op.target = imacro.labelrefs.hasOwnProperty(imm1) ? imacro.labelrefs[imm1] : -1;
367 imacro.labelrefs[imm1] = imacro.code.length;
368 }
369 }
370
371 imacro.code.push(op);
372 imacro.offset += info.oplen;
373 }
374
375 print("uint8 js_opcode2extra[JSOP_LIMIT] = {");
376 for (let i = 0; i < opinfo.length; i++) {
377 print(" " + ((i in opcode2extra) ? opcode2extra[i] : "0") +
378 ", /* " + opinfo[i].jsop + " */");
379 }
380 print("};");
381
382 print("#define JSOP_IS_IMACOP(x) (0 \\");
383 for (let i in opcode2extra)
384 print(" || x == " + opinfo[i].jsop + " \\");
385 print(")");
386
387 print("jsbytecode*\njs_GetImacroStart(jsbytecode* pc) {");
388 for each (let g in igroups) {
389 for each (let m in g.imacros) {
390 let start = g.name + "_imacros." + m.name;
391 print(" if (size_t(pc - " + start + ") < " + m.offset + ") return " + start + ";");
392 }
393 }
394 print(" return NULL;");
395 print("}");
396 }
397
398 for (let i = 0; i < arguments.length; i++) {
399 try {
400 assemble(arguments[i]);
401 } catch (e) {
402 print(e.name + ": " + e.message + "\n" + e.stack);
403 }
404 }

  ViewVC Help
Powered by ViewVC 1.1.24