mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 05:45:37 +00:00
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:
parent
424f3e2bab
commit
cc4b331e7a
@ -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
|
||||
|
@ -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
467
js/src/imacro_asm.py
Normal 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()
|
||||
|
1014
js/src/imacros.c.out
1014
js/src/imacros.c.out
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user