Bug 843462 (part 3) - Use a Vector to build the bytecode. r=jorendorff.

--HG--
extra : rebase_source : b5f266d41f94daf626540f40986f22a1f26c0556
This commit is contained in:
Nicholas Nethercote 2013-02-20 21:13:28 -08:00
parent 9c183b485f
commit 23730fbeba
3 changed files with 80 additions and 121 deletions

View File

@ -46,12 +46,6 @@
#include "frontend/SharedContext-inl.h"
#include "vm/Shape-inl.h"
/* Allocation chunk counts, must be powers of two in general. */
#define BYTECODE_CHUNK_LENGTH 1024 /* initial bytecode chunk length */
/* Macros to compute byte sizes from typed element counts. */
#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode))
using namespace js;
using namespace js::gc;
using namespace js::frontend;
@ -130,43 +124,17 @@ BytecodeEmitter::init()
BytecodeEmitter::~BytecodeEmitter()
{
js_free(prolog.base);
js_free(main.base);
}
static ptrdiff_t
EmitCheck(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
{
jsbytecode *base = bce->base();
jsbytecode *newbase;
jsbytecode *next = bce->next();
jsbytecode *limit = bce->limit();
ptrdiff_t offset = next - base;
size_t minlength = offset + delta;
ptrdiff_t offset = bce->code().length();
if (next + delta > limit) {
size_t newlength;
if (!base) {
JS_ASSERT(!next && !limit);
newlength = BYTECODE_CHUNK_LENGTH;
if (newlength < minlength) /* make it bigger if necessary */
newlength = RoundUpPow2(minlength);
newbase = (jsbytecode *) cx->malloc_(BYTECODE_SIZE(newlength));
} else {
JS_ASSERT(base <= next && next <= limit);
newlength = (limit - base) * 2;
if (newlength < minlength) /* make it bigger if necessary */
newlength = RoundUpPow2(minlength);
newbase = (jsbytecode *) cx->realloc_(base, BYTECODE_SIZE(newlength));
}
if (!newbase) {
js_ReportOutOfMemory(cx);
return -1;
}
JS_ASSERT(newlength >= size_t(offset + delta));
bce->current->base = newbase;
bce->current->limit = newbase + newlength;
bce->current->next = newbase + offset;
jsbytecode dummy = 0;
if (!bce->code().appendN(dummy, delta)) {
js_ReportOutOfMemory(cx);
return -1;
}
return offset;
}
@ -186,7 +154,6 @@ UpdateDepth(JSContext *cx, BytecodeEmitter *bce, ptrdiff_t target)
JSOp op = (JSOp) *pc;
const JSCodeSpec *cs = &js_CodeSpec[op];
if (cs->format & JOF_TMPSLOT_MASK) {
/*
* An opcode may temporarily consume stack space during execution.
@ -235,11 +202,12 @@ ptrdiff_t
frontend::Emit1(JSContext *cx, BytecodeEmitter *bce, JSOp op)
{
ptrdiff_t offset = EmitCheck(cx, bce, 1);
if (offset < 0)
return -1;
if (offset >= 0) {
*bce->current->next++ = (jsbytecode)op;
UpdateDepth(cx, bce, offset);
}
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
UpdateDepth(cx, bce, offset);
return offset;
}
@ -247,14 +215,13 @@ ptrdiff_t
frontend::Emit2(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1)
{
ptrdiff_t offset = EmitCheck(cx, bce, 2);
if (offset < 0)
return -1;
if (offset >= 0) {
jsbytecode *next = bce->next();
next[0] = (jsbytecode)op;
next[1] = op1;
bce->current->next = next + 2;
UpdateDepth(cx, bce, offset);
}
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
code[1] = op1;
UpdateDepth(cx, bce, offset);
return offset;
}
@ -267,15 +234,14 @@ frontend::Emit3(JSContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1,
JS_ASSERT(!IsLocalOp(op));
ptrdiff_t offset = EmitCheck(cx, bce, 3);
if (offset < 0)
return -1;
if (offset >= 0) {
jsbytecode *next = bce->next();
next[0] = (jsbytecode)op;
next[1] = op1;
next[2] = op2;
bce->current->next = next + 3;
UpdateDepth(cx, bce, offset);
}
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
code[1] = op1;
code[2] = op2;
UpdateDepth(cx, bce, offset);
return offset;
}
@ -284,20 +250,20 @@ frontend::EmitN(JSContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra)
{
ptrdiff_t length = 1 + (ptrdiff_t)extra;
ptrdiff_t offset = EmitCheck(cx, bce, length);
if (offset < 0)
return -1;
if (offset >= 0) {
jsbytecode *next = bce->next();
*next = (jsbytecode)op;
memset(next + 1, 0, BYTECODE_SIZE(extra));
bce->current->next = next + length;
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
/* The remaining |extra| bytes are set by the caller */
/*
* Don't UpdateDepth if op's use-count comes from the immediate
* operand yet to be stored in the extra bytes after op.
*/
if (js_CodeSpec[op].nuses >= 0)
UpdateDepth(cx, bce, offset);
/*
* Don't UpdateDepth if op's use-count comes from the immediate
* operand yet to be stored in the extra bytes after op.
*/
if (js_CodeSpec[op].nuses >= 0)
UpdateDepth(cx, bce, offset);
}
return offset;
}
@ -305,14 +271,13 @@ static ptrdiff_t
EmitJump(JSContext *cx, BytecodeEmitter *bce, JSOp op, ptrdiff_t off)
{
ptrdiff_t offset = EmitCheck(cx, bce, 5);
if (offset < 0)
return -1;
if (offset >= 0) {
jsbytecode *next = bce->next();
next[0] = (jsbytecode)op;
SET_JUMP_OFFSET(next, off);
bce->current->next = next + 5;
UpdateDepth(cx, bce, offset);
}
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
SET_JUMP_OFFSET(code, off);
UpdateDepth(cx, bce, offset);
return offset;
}
@ -710,7 +675,7 @@ PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
{
StmtInfoBCE *stmt = bce->topStmt;
if (!stmt->isTrying() &&
(!BackPatch(cx, bce, stmt->breaks, bce->next(), JSOP_GOTO) ||
(!BackPatch(cx, bce, stmt->breaks, bce->code().end(), JSOP_GOTO) ||
!BackPatch(cx, bce, stmt->continues, bce->code(stmt->update), JSOP_GOTO)))
{
return false;
@ -728,10 +693,9 @@ EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
if (offset < 0)
return false;
jsbytecode *next = bce->next();
next[0] = jsbytecode(op);
SET_UINT32_INDEX(next, index);
bce->current->next = next + len;
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
SET_UINT32_INDEX(code, index);
UpdateDepth(cx, bce, offset);
CheckTypeSet(cx, bce, op);
return true;
@ -746,10 +710,9 @@ EmitIndexOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
if (offset < 0)
return false;
jsbytecode *next = bce->next();
next[0] = jsbytecode(op);
SET_UINT32_INDEX(next, index);
bce->current->next = next + len;
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
SET_UINT32_INDEX(code, index);
UpdateDepth(cx, bce, offset);
CheckTypeSet(cx, bce, op);
return true;
@ -795,10 +758,9 @@ EmitAtomIncDec(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce)
if (offset < 0)
return false;
jsbytecode *next = bce->next();
next[0] = jsbytecode(op);
SET_UINT32_INDEX(next, index);
bce->current->next = next + len;
jsbytecode *code = bce->code(offset);
code[0] = jsbytecode(op);
SET_UINT32_INDEX(code, index);
UpdateDepth(cx, bce, offset);
CheckTypeSet(cx, bce, op);
return true;
@ -2502,7 +2464,7 @@ frontend::EmitFunctionScript(JSContext *cx, BytecodeEmitter *bce, ParseNode *bod
FunctionBox *funbox = bce->sc->asFunctionBox();
if (funbox->argumentsHasLocalBinding()) {
JS_ASSERT(bce->next() == bce->base()); /* See JSScript::argumentsBytecode. */
JS_ASSERT(bce->offset() == 0); /* See JSScript::argumentsBytecode. */
bce->switchToProlog();
if (Emit1(cx, bce, JSOP_ARGUMENTS) < 0)
return false;
@ -3418,13 +3380,12 @@ EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn)
if (offset < 0)
return false;
jsbytecode *next = bce->next();
next[0] = JSOP_NEWINIT;
next[1] = jsbytecode(key);
next[2] = 0;
next[3] = 0;
next[4] = 0;
bce->current->next = next + len;
jsbytecode *code = bce->code(offset);
code[0] = JSOP_NEWINIT;
code[1] = jsbytecode(key);
code[2] = 0;
code[3] = 0;
code[4] = 0;
UpdateDepth(cx, bce, offset);
CheckTypeSet(cx, bce, JSOP_NEWINIT);
return true;
@ -3804,7 +3765,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
* Fix up the gosubs that might have been emitted before non-local
* jumps to the finally code.
*/
if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->next(), JSOP_GOSUB))
if (!BackPatch(cx, bce, stmtInfo.gosubs(), bce->code().end(), JSOP_GOSUB))
return false;
finallyStart = bce->offset();
@ -3829,7 +3790,7 @@ EmitTry(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
return false;
/* Fix up the end-of-try/catch jumps to come here. */
if (!BackPatch(cx, bce, catchJump, bce->next(), JSOP_GOTO))
if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO))
return false;
/*
@ -4649,7 +4610,7 @@ EmitReturn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (!EmitNonLocalJumpFixup(cx, bce, NULL))
return false;
if (top + JSOP_RETURN_LENGTH != bce->offset()) {
bce->base()[top] = JSOP_SETRVAL;
bce->code()[top] = JSOP_SETRVAL;
if (Emit1(cx, bce, JSOP_RETRVAL) < 0)
return false;
}
@ -5227,7 +5188,7 @@ EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
* ignore setters and to avoid dup'ing and popping the object as each
* property is added, as JSOP_SETELEM/JSOP_SETPROP would do.
*/
ptrdiff_t offset = bce->next() - bce->base();
ptrdiff_t offset = bce->offset();
if (!EmitNewInit(cx, bce, JSProto_Object, pn))
return false;

View File

@ -61,6 +61,7 @@ struct StmtInfoBCE;
// Use zero inline elements because these go on the stack and affect how many
// nested functions are possible.
typedef Vector<jsbytecode, 0> BytecodeVector;
typedef Vector<jssrcnote, 0> SrcNotesVector;
struct BytecodeEmitter
@ -74,9 +75,7 @@ struct BytecodeEmitter
Rooted<JSScript*> script; /* the JSScript we're ultimately producing */
struct EmitSection {
jsbytecode *base; /* base of JS bytecode vector */
jsbytecode *limit; /* one byte beyond end of bytecode */
jsbytecode *next; /* pointer to next free bytecode */
BytecodeVector code; /* bytecode */
SrcNotesVector notes; /* source notes, see below */
ptrdiff_t lastNoteOffset; /* code offset for last source note */
unsigned currentLine; /* line number for tree-based srcnote gen */
@ -84,9 +83,11 @@ struct BytecodeEmitter
last SRC_COLSPAN-annotated opcode */
EmitSection(JSContext *cx, unsigned lineno)
: base(NULL), limit(NULL), next(NULL), notes(cx), lastNoteOffset(0),
currentLine(lineno), lastColumn(0)
: code(cx), notes(cx), lastNoteOffset(0), currentLine(lineno), lastColumn(0)
{
// Start them off moderately large, to avoid repeated resizings
// early on.
code.reserve(1024);
notes.reserve(1024);
}
};
@ -136,17 +137,17 @@ struct BytecodeEmitter
don't ever get emitted. See the comment for
the field |selfHostingMode| in Parser.h for details. */
/*
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
* space above their tempMark points. This means that you cannot alloc from
* tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
* destruction.
*/
BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
HandleScript script, HandleScript evalCaller, bool hasGlobalScope,
unsigned lineno, bool selfHostingMode = false);
bool init();
/*
* Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
* space above their tempMark points. This means that you cannot alloc from
* tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
* destructor call.
*/
~BytecodeEmitter();
bool isAliasedName(ParseNode *pn);
@ -176,13 +177,10 @@ struct BytecodeEmitter
TokenStream *tokenStream() { return &parser->tokenStream; }
jsbytecode *base() const { return current->base; }
jsbytecode *limit() const { return current->limit; }
jsbytecode *next() const { return current->next; }
jsbytecode *code(ptrdiff_t offset) const { return base() + offset; }
ptrdiff_t offset() const { return next() - base(); }
jsbytecode *prologBase() const { return prolog.base; }
ptrdiff_t prologOffset() const { return prolog.next - prolog.base; }
BytecodeVector &code() const { return current->code; }
jsbytecode *code(ptrdiff_t offset) const { return current->code.begin() + offset; }
ptrdiff_t offset() const { return current->code.end() - current->code.begin(); }
ptrdiff_t prologOffset() const { return prolog.code.end() - prolog.code.begin(); }
void switchToMain() { current = &main; }
void switchToProlog() { current = &prolog; }

View File

@ -1874,8 +1874,8 @@ JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, Bytecode
return false;
jsbytecode *code = ssd->data;
PodCopy<jsbytecode>(code, bce->prologBase(), prologLength);
PodCopy<jsbytecode>(code + prologLength, bce->base(), mainLength);
PodCopy<jsbytecode>(code, bce->prolog.code.begin(), prologLength);
PodCopy<jsbytecode>(code + prologLength, bce->code().begin(), mainLength);
if (!FinishTakingSrcNotes(cx, bce, (jssrcnote *)(code + script->length)))
return false;
InitAtomMap(cx, bce->atomIndices.getMap(), ssd->atoms(script->length, nsrcnotes));