Bug 563728 - Switch the imacro compiler over to Python. r=Waldo.

--HG--
rename : js/src/imacro_asm.js.in => js/src/imacro_asm.py
This commit is contained in:
Jason Orendorff 2010-05-12 08:49:36 -05:00
parent 424f3e2bab
commit cc4b331e7a
4 changed files with 474 additions and 1497 deletions

View File

@ -290,6 +290,7 @@ CPPSRCS += \
LOCAL_INCLUDES = \
-Ictypes/libffi/include \
-I. \
$(NULL)
ifeq ($(OS_ARCH),OS2)
@ -699,29 +700,13 @@ $(CURDIR)/javascript-trace.h: $(srcdir)/javascript-trace.d
$(addsuffix .$(OBJ_SUFFIX),jsdtracef jsinterp jsobj): $(CURDIR)/javascript-trace.h
endif
ifdef GNU_CC
ifndef CROSS_COMPILE
ifndef QEMU_CANT_RUN_JS_SHELL
# If we don't have run-mozilla.sh *and* we're linking against NSPR, we don't
# know how to run the JS binary. Oh well
imacro_asm.js: imacro_asm.js.in jsopcode.tbl
$(CC) -c -x c -E -P -I$(srcdir) $< > $@
GARBAGE += imacros.c.tmp imacro_asm.js
ifneq (,$(wildcard $(RUN_TEST_PROGRAM))$(if $(NSPR_LIBS),,1))
libs:: imacro_asm.js $(srcdir)/imacros.jsasm
$(wildcard $(RUN_TEST_PROGRAM)) $(DIST)/bin/js$(BIN_SUFFIX) $< $(srcdir)/imacros.jsasm > imacros.c.tmp
@cmp -s imacros.c.tmp $(srcdir)/imacros.c.out || \
(echo "imacros.c.out is out of date. Run 'make update-imacros' to copy it to your source tree."; diff -U 4 $(srcdir)/imacros.c.out imacros.c.out; exit 1)
@echo "imacros.c.out is up to date"
update-imacros: imacros.c.tmp
cp $< $(srcdir)/imacros.c.out
ifdef ENABLE_TRACEJIT
# Imacro compilation.
$(CURDIR)/imacros.c.out: $(srcdir)/imacro_asm.py $(srcdir)/imacros.jsasm
$(PYTHON) $< $(srcdir)/imacros.jsasm $(CURDIR)/imacros.c.out
$(addsuffix .$(OBJ_SUFFIX),jstracer): $(CURDIR)/imacros.c.out
# Code for importing the nanojit subtree from its repository.
NANOJIT_CENTRAL_REV=$(shell cat $(srcdir)/nanojit-import-rev)
NANOJIT_CENTRAL_REPO=http://hg.mozilla.org/projects/nanojit-central
NANOJIT_CENTRAL_LOCAL=$(CURDIR)/nanojit-central
@ -747,8 +732,5 @@ update-nanojit:
(cd $(NANOJIT_CENTRAL_LOCAL) && hg log -r tip --template "{node}\n") >$(srcdir)/nanojit-import-rev
(cd $(srcdir) && hg commit --message="Update nanojit-import-rev stamp." nanojit-import-rev)
.PHONY: update-imacros update-nanojit
endif
endif
endif
.PHONY: update-nanojit
endif

View File

@ -1,458 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 ts=8 et tw=78 ft=javascript: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the TraceMonkey IMacro Assembler.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* An imacro (interpreter-macro) assembler in JS, with a light dusting of C
* pre-processor. We depend on the snarf function from the js shell, defined
* originally for the Narcissus metacircular evaluator, now unconditionally
* compiled into the shell.
*
* Filename suffix conventions, used by Makefile.in rules:
* .js.in C-pre-processed JS source
* .jsasm SpiderMonkey JS assembly source, which could be input to other
* assemblers than imacro_asm.js, hence the generic suffix!
* .c.out C source output by imacro_asm.js
*/
#define ASSERT(cond) _ASSERT(cond, #cond)
function _ASSERT(cond, message) {
if (!cond)
throw new Error("Assertion failed: " + message);
}
const js_arguments_str = "arguments";
const js_new_str = "new";
const js_typeof_str = "typeof";
const js_void_str = "void";
const js_null_str = "null";
const js_this_str = "this";
const js_false_str = "false";
const js_true_str = "true";
const js_throw_str = "throw";
const js_in_str = "in";
const js_instanceof_str = "instanceof";
const js_getter_str = "getter";
const js_setter_str = "setter";
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
{jsop: #op, opcode: val, opname: name, opsrc: token, oplen: length, \
pops: nuses, pushes: ndefs, precedence: prec, flags: #format},
const NULL = null;
const opinfo = [
#include "jsopcode.tbl"
];
const opname2info = {};
const jsop2opcode = {};
for (let i = 0; i < opinfo.length; i++) {
let info = opinfo[i];
ASSERT(info.opcode == i);
opname2info[info.opname] = info;
jsop2opcode[info.jsop] = info.opcode;
}
function format_offset(n, w) {
let s = n.toString();
while (s.length < w)
s = ' ' + s;
return s;
}
function immediate(op) {
let info = op.info;
let imm1Expr = /^\(/.test(op.imm1);
if (info.flags.indexOf("JOF_ATOM") >= 0) {
if (/^(?:void|object|function|string|number|boolean)$/.test(op.imm1))
return "0, COMMON_TYPE_ATOM_INDEX(JSTYPE_" + op.imm1.toUpperCase() + ")";
return "0, COMMON_ATOM_INDEX(" + op.imm1 + ")";
}
if (info.flags.indexOf("JOF_JUMP") >= 0) {
ASSERT(!imm1Expr);
return ((op.target >> 8) & 0xff) + ", " + (op.target & 0xff);
}
if (info.flags.indexOf("JOF_UINT8") >= 0 ||
info.flags.indexOf("JOF_INT8") >= 0) {
if (imm1Expr)
return op.imm1;
if (isNaN(Number(op.imm1)) || Number(op.imm1) != parseInt(op.imm1))
throw new Error("invalid 8-bit operand: " + op.imm1);
return (op.imm1 & 0xff);
}
if (info.flags.indexOf("JOF_UINT16") >= 0) {
if (imm1Expr)
return '(_ & 0xff00) >> 8, (_ & 0xff)'.replace(/_/g, op.imm1);
return ((op.imm1 & 0xff00) >> 8) + ", " + (op.imm1 & 0xff);
}
throw new Error(info.jsop + " format not yet implemented");
}
function simulate_cfg(igroup, imacro, depth, i) {
let any_group_opcode = undefined;
let expected_depth = undefined;
for (let opcode in igroup.ops) {
let opi = opinfo[opcode];
if (any_group_opcode === undefined) {
any_group_opcode = opcode;
expected_depth = (opi.pops < 0) ? undefined : opi.pushes - opi.pops;
} else if (expected_depth === undefined) {
if (opi.pops >= 0)
throw Error("imacro shared by constant- and variable-stack-defs/uses instructions");
} else {
if (opi.pops < 0)
throw Error("imacro shared by constant- and variable-stack-defs/uses instructions");
if (opi.pushes - opi.pops != expected_depth)
throw Error("imacro shared by instructions with different stack depths");
}
}
while (i < imacro.code.length) {
let op = imacro.code[i];
let opi = op.info;
if (opi.opname == 'imacop')
opi = opinfo[any_group_opcode];
depth -= (opi.pops < 0) ? 2 + Number(op.imm1) : opi.pops;
depth += opi.pushes;
if (imacro.depths.hasOwnProperty(i) && imacro.depths[i] != depth)
throw Error("Mismatched depth at " + imacro.filename + ":" + op.line);
/*
* Underflowing depth isn't necessarily fatal; most of the imacros
* assume they are called with N>0 args so some assume it's ok to go
* to some depth <N. We simulate starting from 0, as we've no idea
* what else to do.
*
* if (depth < 0)
* throw Error("Negative static-stack depth at " + imacro.filename + ":" + op.line);
*/
if (depth > imacro.maxdepth)
imacro.maxdepth = depth;
imacro.depths[i] = depth;
if (op.hasOwnProperty("target_index")) {
if (op.target_index <= i)
throw Error("Backward jump at " + imacro.filename + ":" + op.line);
simulate_cfg(igroup, imacro, depth, op.target_index);
if (op.info.opname == "goto" || op.info.opname == "gotox")
return;
}
++i;
}
if (expected_depth !== undefined && depth != expected_depth)
throw Error("Expected depth " + expected_depth + ", got " + depth);
}
/*
* Syntax (spaces are significant only to delimit tokens):
*
* Assembly ::= (Directive? '\n')*
* Directive ::= (name ':')? Operation
* Operation ::= opname Operands?
* Operands ::= Operand (',' Operand)*
* Operand ::= name | number | '(' Expr ')'
* Expr ::= a constant-expression in the C++ language
* containing no parentheses
*
* We simplify given line structure and the maximum of one immediate operand,
* by parsing using split and regexps. For ease of parsing, parentheses are
* banned in an Expr for now, even in quotes or a C++ comment.
*
* Pseudo-ops start with . and include .igroup and .imacro, terminated by .end.
* .imacro must nest in .igroup, neither nests in itself. See imacros.jsasm for
* examples.
*/
const line_regexp_parts = [
"^(?:(\\w+):)?", // optional label at start of line
"\\s*(\\.?\\w+)", // optional spaces, (pseudo-)opcode
"(?:\\s+([+-]?\\w+|\\([^)]*\\)))?", // optional first immediate operand
"(?:\\s+([\\w-,]+|\\([^)]*\\)))?", // optional second immediate operand
"(?:\\s*(?:#.*))?$" // optional spaces and comment
];
const line_regexp = new RegExp(line_regexp_parts.join(""));
function assemble(filename) {
let igroup = null, imacro = null;
let opcode2extra = [];
let igroups = [];
print("/* GENERATED BY imacro_asm.js -- DO NOT EDIT!!! */");
let s = snarf(filename);
let a = s.split('\n');
for (let i = 0; i < a.length; i++) {
if (/^\s*(?:#.*)?$/.test(a[i]))
continue;
let m = line_regexp.exec(a[i]);
if (!m)
throw new Error(a[i]);
let [, label, opname, imm1, imm2] = m;
if (opname[0] == '.') {
if (label)
throw new Error("invalid label " + label + " before " + opname);
switch (opname) {
case ".igroup":
if (!imm1)
throw new Error("missing .igroup name");
if (igroup)
throw new Error("nested .igroup " + imm1);
if (!imm2.match(/^\w+(?:-\w+)?(?:,\w+(?:-\w+)?)*$/))
throw new Error("invalid igroup operator range " + imm2);
let ops = imm2.split(",").reduce(function(initial, current) {
let split = current.match(/^(\w+)(?:-(\w+))?$/);
let opcode = jsop2opcode[split[1]];
if (opcode in initial)
throw new Error("repeated opcode " + split[1]);
initial[opcode] = true;
if (split[2] !== undefined) {
let lastopcode = jsop2opcode[split[2]];
if (opcode >= lastopcode)
throw new Error("invalid opcode range: " + current);
while (++opcode <= lastopcode) {
if (opcode in initial)
throw new Error("repeated opcode " + split[2]);
initial[opcode] = true;
}
}
return initial;
}, {});
igroup = {
name: imm1,
ops: ops,
imacros: []
};
break;
case ".imacro":
if (!igroup)
throw new Error(".imacro outside of .igroup");
if (!imm1)
throw new Error("missing .imacro name");
if (imacro)
throw new Error("nested .imacro " + imm1);
imacro = {
name: imm1,
offset: 0,
code: [],
labeldefs: {},
labeldef_indexes: {},
labelrefs: {},
filename: filename,
depths: {},
initdepth: 0,
};
break;
case ".fixup":
if (!imacro)
throw new Error(".fixup outside of .imacro");
if (imacro.code.length !== 0)
throw new Error(".fixup must be first item in .imacro");
if (!imm1)
throw new Error("missing .fixup argument");
let fixup = Number(imm1);
if (isNaN(fixup) || fixup !== parseInt(imm1) || fixup == 0)
throw new Error(".fixup argument must be a nonzero integer");
if (imacro.initdepth !== 0)
throw new Error("more than one .fixup in .imacro");
imacro.initdepth = fixup;
break;
case ".end":
if (!imacro) {
if (!igroup)
throw new Error(".end without prior .igroup or .imacro");
if (imm1 && (imm1 != igroup.name || imm2))
throw new Error(".igroup/.end name mismatch");
let maxdepth = 0;
print("static struct {");
for (let j = 0; j < igroup.imacros.length; j++) {
imacro = igroup.imacros[j];
print(" jsbytecode " + imacro.name + "[" + imacro.offset + "];");
}
print("} " + igroup.name + "_imacros = {");
for (let j = 0; j < igroup.imacros.length; j++) {
let depth = 0;
imacro = igroup.imacros[j];
print(" {");
for (let k = 0; k < imacro.code.length; k++) {
let op = imacro.code[k];
print("/*" + format_offset(op.offset,2) + "*/ " + op.info.jsop +
(op.imm1 ? ", " + immediate(op) : "") + ",");
}
imacro.maxdepth = imacro.initdepth;
simulate_cfg(igroup, imacro, imacro.initdepth, 0);
if (imacro.maxdepth > maxdepth)
maxdepth = imacro.maxdepth;
print(" },");
}
print("};");
for (let opcode in igroup.ops)
opcode2extra[opcode] = maxdepth;
igroups.push(igroup);
igroup = null;
} else {
ASSERT(igroup);
if (imm1 && imm1 != imacro.name)
throw new Error(".imacro/.end name mismatch");
// Backpatch the forward references to labels that must now be defined.
for (label in imacro.labelrefs) {
if (!imacro.labelrefs.hasOwnProperty(label))
continue;
if (!imacro.labeldefs.hasOwnProperty(label))
throw new Error("label " + label + " used but not defined");
let link = imacro.labelrefs[label];
ASSERT(link >= 0);
for (;;) {
let op = imacro.code[link];
ASSERT(op);
ASSERT(op.hasOwnProperty('target'));
let next = op.target;
op.target = imacro.labeldefs[label] - op.offset;
op.target_index = imacro.labeldef_indexes[label];
if (next < 0)
break;
link = next;
}
}
igroup.imacros.push(imacro);
}
imacro = null;
break;
default:
throw new Error("unknown pseudo-op " + opname);
}
continue;
}
if (!opname2info.hasOwnProperty(opname))
throw new Error("unknown opcode " + opname + (label ? " (label " + label + ")" : ""));
let info = opname2info[opname];
if (info.oplen == -1)
throw new Error("unimplemented opcode " + opname);
if (!imacro)
throw new Error("opcode " + opname + " outside of .imacro");
// Blacklist ops that may or must use an atomized double immediate.
switch (info.opname) {
case "double":
case "lookupswitch":
case "lookupswitchx":
throw new Error(op.opname + " opcode not yet supported");
}
if (label) {
imacro.labeldefs[label] = imacro.offset;
imacro.labeldef_indexes[label] = imacro.code.length;
}
let op = {offset: imacro.offset, info: info, imm1: imm1, imm2: imm2, line:(i+1) };
if (info.flags.indexOf("JOF_JUMP") >= 0) {
if (imacro.labeldefs.hasOwnProperty(imm1)) {
// Backward reference can be resolved right away, no backpatching needed.
op.target = imacro.labeldefs[imm1] - op.offset;
op.target_index = imacro.labeldef_indexes[imm1];
} else {
// Link op into the .target-linked backpatch chain at labelrefs[imm1].
// The linked list terminates with a -1 sentinel.
op.target = imacro.labelrefs.hasOwnProperty(imm1) ? imacro.labelrefs[imm1] : -1;
imacro.labelrefs[imm1] = imacro.code.length;
}
}
imacro.code.push(op);
imacro.offset += info.oplen;
}
print("uint8 js_opcode2extra[JSOP_LIMIT] = {");
for (let i = 0; i < opinfo.length; i++) {
print(" " + ((i in opcode2extra) ? opcode2extra[i] : "0") +
", /* " + opinfo[i].jsop + " */");
}
print("};");
print("#define JSOP_IS_IMACOP(x) (0 \\");
for (let i in opcode2extra)
print(" || x == " + opinfo[i].jsop + " \\");
print(")");
print("jsbytecode*\njs_GetImacroStart(jsbytecode* pc) {");
for each (let g in igroups) {
for each (let m in g.imacros) {
let start = g.name + "_imacros." + m.name;
print(" if (size_t(pc - " + start + ") < " + m.offset + ") return " + start + ";");
}
}
print(" return NULL;");
print("}");
}
for (let i = 0; i < arguments.length; i++) {
try {
assemble(arguments[i]);
} catch (e) {
print(e.name + ": " + e.message + "\n" + e.stack);
}
}

467
js/src/imacro_asm.py Normal file
View File

@ -0,0 +1,467 @@
#!/usr/bin/env python
# -*- Mode: Python; tab-width: 4; indent-tabs-mode: nil -*-
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the TraceMonkey IMacro Assembler.
#
# The Initial Developer of the Original Code is
# Brendan Eich <brendan@mozilla.org>.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
# An imacro (interpreter-macro) assembler in Python.
#
# Filename suffix conventions, used by Makefile.in rules:
# .jsasm SpiderMonkey JS assembly source, which could be input to other
# assemblers than imacro_asm.js, hence the generic suffix!
# .c.out C source output by imacro_asm.js
import re
import os
class Op:
def __init__(self, jsop, opcode, opname, opsrc, oplen, pops, pushes, precedence, flags):
self.jsop = jsop
self.opcode = opcode
self.opname = opname
self.opsrc = opsrc
self.oplen = oplen
self.pops = pops
self.pushes = pushes
self.precedence = precedence
self.flags = flags
def readFileLines(filename):
f = open(filename)
try:
return f.readlines()
finally:
f.close()
def load_ops(filename):
opdef_regexp = re.compile(r'''(?x)
^ OPDEF \( (JSOP_\w+), \s* # op
([0-9]+), \s* # val
("[^"]+" | [\w_]+), \s* # name
("[^"]+" | [\w_]+), \s* # image
(-1|[0-9]+), \s* # len
(-1|[0-9]+), \s* # uses
(-1|[0-9]+), \s* # defs
([0-9]+), \s* # prec
([\w_| ]+) \s* # format
\) \s* $''')
def decode_string_expr(expr):
if expr == 'NULL':
return None
if expr[0] == '"':
assert expr[-1] == '"'
return expr[1:-1]
assert expr.startswith('js_') and expr.endswith('_str')
return expr[3:-4]
opinfo = []
for lineno, line in enumerate(readFileLines(filename)):
if line.startswith('OPDEF'):
m = opdef_regexp.match(line)
if m is None:
raise ValueError("OPDEF line of wrong format in jsopcode.tbl at line %d" % (lineno + 1))
jsop, opcode, opname, opsrc, oplen, pops, pushes, precedence, flags = m.groups()
assert int(opcode) == len(opinfo)
opinfo.append(Op(jsop, int(opcode), decode_string_expr(opname),
decode_string_expr(opsrc), int(oplen), int(pops), int(pushes),
int(precedence), flags.replace(' ', '').split('|')))
return opinfo
opinfo = load_ops(os.path.join(os.path.dirname(__file__), "jsopcode.tbl"))
opname2info = dict((info.opname, info) for info in opinfo)
jsop2opcode = dict((info.jsop, info.opcode) for info in opinfo)
def to_uint8(s):
try:
n = int(s)
except ValueError:
n = -1
if 0 <= n < (1<<8):
return n
raise ValueError("invalid 8-bit operand: " + s)
def to_uint16(s):
try:
n = int(s)
except ValueError:
n = -1
if 0 <= n < (1<<16):
return n
raise ValueError("invalid 16-bit operand: " + s)
def immediate(op):
info = op.info
imm1Expr = op.imm1.startswith('(')
if 'JOF_ATOM' in info.flags:
if op.imm1 in ('void', 'object', 'function', 'string', 'number', 'boolean'):
return "0, COMMON_TYPE_ATOM_INDEX(JSTYPE_%s)" % op.imm1.upper()
return "0, COMMON_ATOM_INDEX(%s)" % op.imm1
if 'JOF_JUMP' in info.flags:
assert not imm1Expr
return "%d, %d" % ((op.target >> 8) & 0xff, op.target & 0xff)
if 'JOF_UINT8' in info.flags or 'JOF_INT8' in info.flags:
if imm1Expr:
return op.imm1
return str(to_uint8(op.imm1))
if 'JOF_UINT16' in info.flags:
if imm1Expr:
return '(%s & 0xff00) >> 8, (%s & 0xff)' % (op.imm1, op.imm1)
v = to_uint16(op.imm1)
return "%d, %d" % ((v & 0xff00) >> 8, v & 0xff)
raise NotImplementedError(info.jsop + " format not yet implemented")
def simulate_cfg(igroup, imacro, depth, i):
any_group_opcode = None
expected_depth = None
for opcode in igroup.ops:
opi = opinfo[opcode]
if any_group_opcode is None:
any_group_opcode = opcode
if opi.pops < 0:
expected_depth = None
else:
expected_depth = opi.pushes - opi.pops
elif expected_depth is None:
if opi.pops >= 0:
raise ValueError("imacro shared by constant- and variable-stack-defs/uses instructions")
else:
if opi.pops < 0:
raise ValueError("imacro shared by constant- and variable-stack-defs/uses instructions")
if opi.pushes - opi.pops != expected_depth:
raise ValueError("imacro shared by instructions with different stack depths")
for i in range(i, len(imacro.code)):
op = imacro.code[i]
opi = op.info
if opi.opname == 'imacop':
opi = opinfo[any_group_opcode]
if opi.pops < 0:
depth -= 2 + int(op.imm1)
else:
depth -= opi.pops
depth += opi.pushes
if i in imacro.depths and imacro.depths[i] != depth:
raise ValueError("Mismatched depth at %s:%d" % (imacro.filename, op.line))
# Underflowing depth isn't necessarily fatal; most of the imacros
# assume they are called with N>0 args so some assume it's ok to go
# to some depth <N. We simulate starting from 0, as we've no idea
# what else to do.
#
# if depth < 0:
# raise ValueError("Negative static-stack depth at %s:%d" % (imacro.filename, op.line))
if depth > imacro.maxdepth:
imacro.maxdepth = depth
imacro.depths[i] = depth
if hasattr(op, "target_index"):
if op.target_index <= i:
raise ValueError("Backward jump at %s:%d" % (imacro.filename, op.line))
simulate_cfg(igroup, imacro, depth, op.target_index)
if op.info.opname in ('goto', 'gotox'):
return
if expected_depth is not None and depth != expected_depth:
raise ValueError("Expected depth %d, got %d" % (expected_depth, depth))
# Syntax (spaces are significant only to delimit tokens):
#
# Assembly ::= (Directive? '\n')*
# Directive ::= (name ':')? Operation
# Operation ::= opname Operands?
# Operands ::= Operand (',' Operand)*
# Operand ::= name | number | '(' Expr ')'
# Expr ::= a constant-expression in the C++ language
# containing no parentheses
#
# We simplify given line structure and the maximum of one immediate operand,
# by parsing using split and regexps. For ease of parsing, parentheses are
# banned in an Expr for now, even in quotes or a C++ comment.
#
# Pseudo-ops start with . and include .igroup and .imacro, terminated by .end.
# .imacro must nest in .igroup, neither nests in itself. See imacros.jsasm for
# examples.
#
line_regexp = re.compile(r'''(?x)
^
(?: (\w+): )? # optional label at start of line
\s* (\.?\w+) # optional spaces, (pseudo-)opcode
(?: \s+ ([+-]?\w+ | \([^)]*\)) )? # optional first immediate operand
(?: \s+ ([\w,-]+ | \([^)]*\)) )? # optional second immediate operand
(?: \s* (?:\#.*) )? # optional spaces and comment
$''')
oprange_regexp = re.compile(r'^\w+(?:-\w+)?(?:,\w+(?:-\w+)?)*$')
class IGroup(object):
def __init__(self, name, ops):
self.name = name
self.ops = ops
self.imacros = []
class IMacro(object):
def __init__(self, name, filename):
self.name = name
self.offset = 0
self.code = []
self.labeldefs = {}
self.labeldef_indexes = {}
self.labelrefs = {}
self.filename = filename
self.depths = {}
self.initdepth = 0
class Instruction(object):
def __init__(self, offset, info, imm1, imm2, lineno):
self.offset = offset
self.info = info
self.imm1 = imm1
self.imm2 = imm2
self.lineno = lineno
def assemble(filename, outfile):
write = outfile.write
igroup = None
imacro = None
opcode2extra = {}
igroups = []
write("/* GENERATED BY imacro_asm.js -- DO NOT EDIT!!! */\n")
def fail(msg, *args):
raise ValueError("%s at %s:%d" % (msg % args, filename, lineno + 1))
for lineno, line in enumerate(readFileLines(filename)):
# strip comments
line = re.sub(r'#.*', '', line).rstrip()
if line == "":
continue
m = line_regexp.match(line)
if m is None:
fail(line)
label, opname, imm1, imm2 = m.groups()
if opname.startswith('.'):
if label is not None:
fail("invalid label %s before %s" % (label, opname))
if opname == '.igroup':
if imm1 is None:
fail("missing .igroup name")
if igroup is not None:
fail("nested .igroup " + imm1)
if oprange_regexp.match(imm2) is None:
fail("invalid igroup operator range " + imm2)
ops = set()
for current in imm2.split(","):
split = current.split('-')
opcode = jsop2opcode[split[0]]
if len(split) == 1:
lastopcode = opcode
else:
assert len(split) == 2
lastopcode = jsop2opcode[split[1]]
if opcode >= lastopcode:
fail("invalid opcode range: " + current)
for opcode in range(opcode, lastopcode + 1):
if opcode in ops:
fail("repeated opcode " + opinfo[opcode].jsop)
ops.add(opcode)
igroup = IGroup(imm1, ops)
elif opname == '.imacro':
if igroup is None:
fail(".imacro outside of .igroup")
if imm1 is None:
fail("missing .imacro name")
if imacro:
fail("nested .imacro " + imm1)
imacro = IMacro(imm1, filename)
elif opname == '.fixup':
if imacro is None:
fail(".fixup outside of .imacro")
if len(imacro.code) != 0:
fail(".fixup must be first item in .imacro")
if imm1 is None:
fail("missing .fixup argument")
try:
fixup = int(imm1)
except ValueError:
fail(".fixup argument must be a nonzero integer")
if fixup == 0:
fail(".fixup argument must be a nonzero integer")
if imacro.initdepth != 0:
fail("more than one .fixup in .imacro")
imacro.initdepth = fixup
elif opname == '.end':
if imacro is None:
if igroup is None:
fail(".end without prior .igroup or .imacro")
if imm1 is not None and (imm1 != igroup.name or imm2 is not None):
fail(".igroup/.end name mismatch")
maxdepth = 0
write("static struct {\n")
for imacro in igroup.imacros:
write(" jsbytecode %s[%d];\n" % (imacro.name, imacro.offset))
write("} %s_imacros = {\n" % igroup.name)
for imacro in igroup.imacros:
depth = 0
write(" {\n")
for op in imacro.code:
operand = ""
if op.imm1 is not None:
operand = ", " + immediate(op)
write("/*%2d*/ %s%s,\n" % (op.offset, op.info.jsop, operand))
imacro.maxdepth = imacro.initdepth
simulate_cfg(igroup, imacro, imacro.initdepth, 0)
if imacro.maxdepth > maxdepth:
maxdepth = imacro.maxdepth
write(" },\n")
write("};\n")
for opcode in igroup.ops:
opcode2extra[opcode] = maxdepth
igroups.append(igroup)
igroup = None
else:
assert igroup is not None
if imm1 is not None and imm1 != imacro.name:
fail(".imacro/.end name mismatch")
# Backpatch the forward references to labels that must now be defined.
for label in imacro.labelrefs:
if label not in imacro.labeldefs:
fail("label " + label + " used but not defined")
link = imacro.labelrefs[label]
assert link >= 0
while True:
op = imacro.code[link]
next = op.target
op.target = imacro.labeldefs[label] - op.offset
op.target_index = imacro.labeldef_indexes[label]
if next < 0:
break
link = next
igroup.imacros.append(imacro)
imacro = None
else:
fail("unknown pseudo-op " + opname)
continue
if opname not in opname2info:
fail("unknown opcode " + opname)
info = opname2info[opname]
if info.oplen == -1:
fail("unimplemented opcode " + opname)
if imacro is None:
fail("opcode %s outside of .imacro", opname)
# Blacklist ops that may or must use an atomized double immediate.
if info.opname in ('double', 'lookupswitch', 'lookupswitchx'):
fail(op.opname + " opcode not yet supported")
if label:
imacro.labeldefs[label] = imacro.offset
imacro.labeldef_indexes[label] = len(imacro.code)
op = Instruction(imacro.offset, info, imm1, imm2, lineno + 1)
if 'JOF_JUMP' in info.flags:
if imm1 in imacro.labeldefs:
# Backward reference can be resolved right away, no backpatching needed.
op.target = imacro.labeldefs[imm1] - op.offset
op.target_index = imacro.labeldef_indexes[imm1]
else:
# Link op into the .target-linked backpatch chain at labelrefs[imm1].
# The linked list terminates with a -1 sentinel.
if imm1 in imacro.labelrefs:
op.target = imacro.labelrefs[imm1]
else:
op.target = -1
imacro.labelrefs[imm1] = len(imacro.code)
imacro.code.append(op)
imacro.offset += info.oplen
write("uint8 js_opcode2extra[JSOP_LIMIT] = {\n")
for i in range(len(opinfo)):
write(" %d, /* %s */\n" % (opcode2extra.get(i, 0), opinfo[i].jsop))
write("};\n")
write("#define JSOP_IS_IMACOP(x) (0 \\\n")
for i in sorted(opcode2extra):
write(" || x == %s \\\n" % opinfo[i].jsop)
write(")\n")
write("jsbytecode*\njs_GetImacroStart(jsbytecode* pc) {\n")
for g in igroups:
for m in g.imacros:
start = g.name + "_imacros." + m.name
write(" if (size_t(pc - %s) < %d) return %s;\n" % (start, m.offset, start))
write(" return NULL;\n")
write("}\n")
if __name__ == '__main__':
import sys
if len(sys.argv) != 3:
print "usage: python imacro_asm.py infile.jsasm outfile.c.out"
sys.exit(1)
f = open(sys.argv[2], 'w')
try:
assemble(sys.argv[1], f)
finally:
f.close()

File diff suppressed because it is too large Load Diff