netscape-revival
mocha/src/mo_emit.c
/*
** Mocha bytecode generation.
**
** Brendan Eich, 6/20/95
*/
#include <memory.h>
#include <stddef.h>
#include <string.h>
#include "prarena.h"
#include "prlog.h"
#include "mo_bcode.h"
#include "mo_cntxt.h"
#include "mo_emit.h"
#include "mochaapi.h"
#define CGINCR 256 /* code generation bytecode allocation increment */
#define SNINCR 64 /* source note allocation increment */
MochaBoolean
mocha_InitCodeGenerator(MochaContext *mc, CodeGenerator *cg, PRArenaPool *ap)
{
cg->pool = ap;
PR_ARENA_ALLOCATE(cg->base, ap, CGINCR);
if (!cg->base) {
MOCHA_ReportOutOfMemory(mc);
return MOCHA_FALSE;
}
cg->limit = CG_CODE(cg, CGINCR);
CG_RESET(cg);
return MOCHA_TRUE;
}
static ptrdiff_t
EmitCheck(MochaContext *mc, CodeGenerator *cg, MochaOp op, int delta)
{
ptrdiff_t offset, length;
PR_ASSERT(delta < CGINCR);
cg->lastOpcode = op;
offset = CG_OFFSET(cg);
if ((uprword_t)cg->ptr + delta >= (uprword_t)cg->limit) {
length = cg->limit - cg->base;
PR_ARENA_GROW(cg->base, cg->pool, length, CGINCR);
if (!cg->base) {
MOCHA_ReportOutOfMemory(mc);
return -1;
}
cg->limit = CG_CODE(cg, length + CGINCR);
cg->ptr = CG_CODE(cg, offset);
}
return offset;
}
void
mocha_UpdateDepth(MochaContext *mc, CodeGenerator *cg, ptrdiff_t target)
{
MochaCode *pc;
SourceNote *sn, *last;
ptrdiff_t offset;
MochaCodeSpec *cs;
int nuses;
pc = CG_CODE(cg, target);
/* Don't worry about hidden MOP_LEAVEs, we guarantee stack balance. */
if (cg->noteCount) {
sn = cg->notes;
last = &sn[cg->noteCount-1];
if (SN_TYPE(last) == SRC_HIDDEN) {
/* We must ensure that this source note applies to target. */
for (offset = 0; sn <= last; sn = SN_NEXT(sn)) {
offset += SN_DELTA(sn);
if (offset == target && sn == last)
return;
}
}
}
cs = &mocha_CodeSpec[pc[0]];
nuses = cs->nuses;
if (nuses < 0)
nuses = pc[1] + 1;
cg->stackDepth -= nuses;
if (cg->stackDepth < 0) {
MOCHA_ReportError(mc, "internal error: stack underflow at pc %d",
target);
}
cg->stackDepth += cs->ndefs;
if (cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
}
ptrdiff_t
mocha_Emit1(MochaContext *mc, CodeGenerator *cg, MochaOp op)
{
ptrdiff_t offset = EmitCheck(mc, cg, op, 1);
if (offset >= 0) {
*cg->ptr++ = op;
mocha_UpdateDepth(mc, cg, offset);
}
return offset;
}
ptrdiff_t
mocha_Emit2(MochaContext *mc, CodeGenerator *cg, MochaOp op, MochaCode op1)
{
ptrdiff_t offset = EmitCheck(mc, cg, op, 2);
if (offset >= 0) {
cg->ptr[0] = op;
cg->ptr[1] = op1;
cg->ptr += 2;
mocha_UpdateDepth(mc, cg, offset);
}
return offset;
}
ptrdiff_t
mocha_Emit3(MochaContext *mc, CodeGenerator *cg, MochaOp op, MochaCode op1,
MochaCode op2)
{
ptrdiff_t offset = EmitCheck(mc, cg, op, 3);
if (offset >= 0) {
cg->ptr[0] = op;
cg->ptr[1] = op1;
cg->ptr[2] = op2;
cg->ptr += 3;
mocha_UpdateDepth(mc, cg, offset);
}
return offset;
}
MochaBoolean
mocha_IsWithStatementDepth(CodeGenerator *cg, int depth)
{
uint32 bit;
PR_ASSERT((unsigned)depth < 32);
bit = 1 << depth;
return (cg->depthTypeSet & bit) ? MOCHA_TRUE : MOCHA_FALSE;
}
MochaBoolean
mocha_SetStatementDepthType(MochaContext *mc, CodeGenerator *cg,
MochaBoolean isWith)
{
uint32 bit;
bit = (uint32)cg->withDepth;
if (bit >= 32) {
MOCHA_ReportError(mc, "too much with and for-in statement nesting");
return MOCHA_FALSE;
}
bit = 1 << bit;
if (isWith)
cg->depthTypeSet |= bit;
else
cg->depthTypeSet &= ~bit;
return MOCHA_TRUE;
}
ptrdiff_t
mocha_MoveCode(MochaContext *mc,
CodeGenerator *from, ptrdiff_t fromOffset,
CodeGenerator *to, ptrdiff_t toOffset)
{
ptrdiff_t length, growth, offset;
length = CG_OFFSET(from) - fromOffset;
growth = (toOffset + length) - CG_OFFSET(to);
offset = EmitCheck(mc, to, from->lastOpcode, growth);
if (offset >= 0) {
memmove(CG_CODE(to, toOffset), CG_CODE(from, fromOffset), length);
to->ptr += growth;
if (from->maxStackDepth > to->maxStackDepth)
to->maxStackDepth = from->maxStackDepth;
}
return offset;
}
void
mocha_PushLoopInfo(CodeGenerator *cg, LoopInfo *li, ptrdiff_t top)
{
SET_LOOPINFO_TOP(li, top);
li->withDepth = 0;
li->down = cg->loopInfo;
cg->loopInfo = li;
}
static ptrdiff_t
EmitLoopGoto(MochaContext *mc, CodeGenerator *cg, LoopInfo *li, ptrdiff_t *last)
{
int i;
ptrdiff_t offset, delta;
for (i = 0; i < li->withDepth; i++) {
if (mocha_NewSourceNote(mc, cg, SRC_HIDDEN) < 0 ||
mocha_Emit1(mc, cg, MOP_LEAVE) < 0) {
return -1;
}
}
offset = CG_OFFSET(cg);
delta = offset - *last;
*last = offset;
return mocha_Emit3(mc, cg, MOP_GOTO,
(MochaCode)(delta >> 8), (MochaCode)delta);
}
static void
PatchLoopGotos(CodeGenerator *cg, LoopInfo *li, ptrdiff_t last,
MochaCode *target)
{
ptrdiff_t delta, jumpOffset;
MochaCode *pc, *top;
pc = CG_CODE(cg, last);
top = CG_CODE(cg, li->top);
while (pc > top) {
delta = GET_JUMP_OFFSET(pc);
jumpOffset = target - pc;
SET_JUMP_OFFSET(pc, jumpOffset);
pc -= delta;
}
}
ptrdiff_t
mocha_EmitBreak(MochaContext *mc, CodeGenerator *cg)
{
return EmitLoopGoto(mc, cg, cg->loopInfo, &cg->loopInfo->breaks);
}
ptrdiff_t
mocha_EmitContinue(MochaContext *mc, CodeGenerator *cg)
{
return mocha_NewSourceNote(mc, cg, SRC_CONTINUE) >= 0 &&
EmitLoopGoto(mc, cg, cg->loopInfo, &cg->loopInfo->continues);
}
void
mocha_PopLoopInfo(CodeGenerator *cg)
{
LoopInfo *li;
li = cg->loopInfo;
PatchLoopGotos(cg, li, li->breaks, cg->ptr);
PatchLoopGotos(cg, li, li->continues, CG_CODE(cg, li->update));
cg->loopInfo = li->down;
}
int
mocha_NewSourceNote(MochaContext *mc, CodeGenerator *cg, SourceNoteType type)
{
int index, index2;
PRArenaPool *ap;
SourceNote *sn;
size_t incr, size;
ptrdiff_t offset, delta;
index = cg->noteCount;
if (index % SNINCR == 0) {
ap = &mc->tempPool;
incr = SNINCR * sizeof(SourceNote);
if (!cg->notes) {
PR_ARENA_ALLOCATE(cg->notes, ap, incr);
} else {
size = cg->noteCount * sizeof(SourceNote);
PR_ARENA_GROW(cg->notes, ap, size, incr);
}
if (!cg->notes) {
MOCHA_ReportOutOfMemory(mc);
return -1;
}
}
sn = &cg->notes[cg->noteCount++];
offset = CG_OFFSET(cg);
delta = offset - cg->lastOffset;
cg->lastOffset = offset;
if (delta >= SN_DELTA_MAX) {
SN_SET_TYPE(sn, SRC_NULL);
do {
SN_SET_DELTA(sn, SN_DELTA_MAX - 1);
delta -= SN_DELTA(sn);
index2 = mocha_NewSourceNote(mc, cg, SRC_NULL);
if (index2 < 0)
return -1;
sn = &cg->notes[index2];
} while (delta >= SN_DELTA_MAX);
}
SN_SET_TYPE(sn, type);
SN_SET_DELTA(sn, delta);
switch (type) {
case SRC_FOR:
if (mocha_NewSourceNote(mc, cg, SRC_NULL) < 0 ||
mocha_NewSourceNote(mc, cg, SRC_NULL) < 0) {
return -1;
}
/* FALL THROUGH */
case SRC_SETLINE:
if (mocha_NewSourceNote(mc, cg, SRC_NULL) < 0)
return -1;
break;
default:;
}
return index;
}
MochaBoolean
mocha_MoveSourceNotes(MochaContext *mc, CodeGenerator *from, CodeGenerator *to)
{
int i, len, index;
SourceNote *sn;
SourceNoteType type;
ptrdiff_t delta;
for (i = 0; i < from->noteCount; i += len) {
len = SN_LENGTH(&from->notes[i]);
type = SN_TYPE(&from->notes[i]);
index = mocha_NewSourceNote(mc, to, type);
if (index < 0)
return MOCHA_FALSE;
sn = &to->notes[index];
switch (type) {
case SRC_FOR:
SN_SET_OFFSET(&sn[3], SN_OFFSET(&from->notes[i + 3]));
SN_SET_OFFSET(&sn[2], SN_OFFSET(&from->notes[i + 2]));
/* FALL THROUGH */
case SRC_SETLINE:
SN_SET_OFFSET(&sn[1], SN_OFFSET(&from->notes[i + 1]));
break;
default:
PR_ASSERT(len == 1);
}
delta = SN_DELTA(&from->notes[i]);
SN_SET_DELTA(&sn[0], delta);
to->lastOffset += delta;
}
return MOCHA_TRUE;
}
SourceNote *
mocha_FinishTakingSourceNotes(MochaContext *mc, CodeGenerator *cg)
{
unsigned len;
SourceNote *tmp, *final;
len = cg->noteCount;
tmp = cg->notes;
final = MOCHA_malloc(mc, (len + 1) * sizeof(SourceNote));
if (!final)
return 0;
memcpy(final, tmp, len * sizeof(SourceNote));
SN_MAKE_TERMINATOR(&final[len]);
CG_RESET_NOTES(cg);
return final;
}
SourceNote *
mocha_GetSourceNote(MochaScript *script, MochaCode *pc)
{
SourceNote *sn;
ptrdiff_t offset, target;
sn = script->notes;
if (!sn) return 0;
target = pc - script->code;
if ((unsigned)target >= script->length)
return 0;
for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
offset += SN_DELTA(sn);
if (offset == target && SN_TYPE(sn) > (unsigned)SRC_SETLINE)
return sn;
}
return 0;
}
unsigned
mocha_PCtoLineNumber(MochaScript *script, MochaCode *pc)
{
SourceNote *sn;
ptrdiff_t offset, target;
unsigned lineno;
SourceNoteType type;
MochaBoolean found;
sn = script->notes;
if (!sn) return 0;
target = pc - script->code;
if ((unsigned)target >= script->length)
return 0;
lineno = script->lineno;
found = MOCHA_FALSE;
for (offset = 0; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
offset += SN_DELTA(sn);
if (offset > target)
found = MOCHA_TRUE;
type = SN_TYPE(sn);
if (type == SRC_SETLINE) {
lineno = SN_OFFSET(&sn[1]);
} else if (type == SRC_NEWLINE) {
lineno++;
if (found)
break;
}
}
return lineno;
}
MochaScript *
mocha_NewScript(MochaContext *mc, CodeGenerator *cg, const char *filename,
unsigned lineno)
{
MochaScript *script;
ptrdiff_t length;
length = CG_OFFSET(cg);
script = MOCHA_malloc(mc, sizeof(MochaScript) + length);
if (!script)
return 0;
memset(script, 0, sizeof(MochaScript));
if (!mocha_InitAtomMap(mc, &script->atomMap, cg)) {
MOCHA_free(mc, script);
return 0;
}
if (filename) {
script->filename = MOCHA_strdup(mc, filename);
if (!script->filename) {
mocha_DestroyScript(mc, script);
return 0;
}
}
script->notes = mocha_FinishTakingSourceNotes(mc, cg);
if (!script->notes) {
mocha_DestroyScript(mc, script);
return 0;
}
script->code = (MochaCode *)(script + 1);
memcpy(script->code, cg->base, length);
script->length = length;
script->depth = cg->maxStackDepth;
script->lineno = lineno;
return script;
}
void
mocha_DestroyScript(MochaContext *mc, MochaScript *script)
{
mocha_FreeAtomMap(mc, &script->atomMap);
if (script->filename)
MOCHA_free(mc, script->filename);
if (script->notes)
MOCHA_free(mc, script->notes);
MOCHA_free(mc, script);
}