pjs/js/src/jsarena.c

561 строка
18 KiB
C
Исходник Обычный вид История

2001-09-20 04:02:59 +04:00
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
2001-09-20 04:02:59 +04:00
* The contents of this file are subject to the Netscape 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
2001-09-20 04:02:59 +04:00
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
2001-09-20 04:02:59 +04:00
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
2001-09-20 04:02:59 +04:00
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
2001-09-20 04:02:59 +04:00
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
2001-09-20 04:02:59 +04:00
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
* provisions of the GPL are applicable instead of those above.
* If you wish to allow use of your version of this file only
* under the terms of the GPL and not to allow others to use your
* version of this file under the NPL, indicate your decision by
* deleting the provisions above and replace them with the notice
* and other provisions required by the GPL. If you do not delete
* the provisions above, a recipient may use your version of this
* file under either the NPL or the GPL.
*/
/*
* Lifetime-based fast allocation, inspired by much prior art, including
* "Fast Allocation and Deallocation of Memory Based on Object Lifetimes"
* David R. Hanson, Software -- Practice and Experience, Vol. 20(1).
*/
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include "jstypes.h"
#include "jsbit.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jslock.h"
static JSArena *arena_freelist;
#ifdef JS_THREADSAFE
static JSLock *arena_freelist_lock;
#endif
#ifdef JS_ARENAMETER
static JSArenaStats *arena_stats_list;
#define COUNT(pool,what) (pool)->stats.what++
#else
#define COUNT(pool,what) /* nothing */
#endif
#define JS_ARENA_DEFAULT_ALIGN sizeof(double)
JS_PUBLIC_API(void)
JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size, size_t align)
{
#ifdef JS_THREADSAFE
/* Must come through here once in primordial thread to init safely! */
if (!arena_freelist_lock) {
arena_freelist_lock = JS_NEW_LOCK();
JS_ASSERT(arena_freelist_lock);
}
#endif
if (align == 0)
align = JS_ARENA_DEFAULT_ALIGN;
pool->mask = JS_BITMASK(JS_CeilingLog2(align));
pool->first.next = NULL;
pool->first.base = pool->first.avail = pool->first.limit =
JS_ARENA_ALIGN(pool, &pool->first + 1);
pool->current = &pool->first;
pool->arenasize = size;
#ifdef JS_ARENAMETER
memset(&pool->stats, 0, sizeof pool->stats);
pool->stats.name = strdup(name);
pool->stats.next = arena_stats_list;
arena_stats_list = &pool->stats;
#endif
}
/*
* An allocation that consumes more than pool->arenasize also has a header
* pointing back to its previous arena's next member. This header is not
* included in [a->base, a->limit), so its space can't be wrongly claimed.
*
* As the header is a pointer, it must be well-aligned. If pool->mask is
* greater than or equal to POINTER_MASK, the header just preceding a->base
* for an oversized arena a is well-aligned, because a->base is well-aligned.
* However, we may need to add more space to pad the JSArena ** back-pointer
* so that it lies just behind a->base, because a might not be aligned such
* that (jsuword)(a + 1) is on a pointer boundary.
*
* By how much must we pad? Let M be the alignment modulus for pool and P
* the modulus for a pointer. Given M >= P, the greatest distance between a
* pointer aligned on an M boundary and one aligned on a P boundary is M-P.
* If M and P are powers of two, then M-P = (pool->mask - POINTER_MASK).
*
* How much extra padding might spill over unused into the remainder of the
* allocation, in the worst case (where M > P)?
*
* If we add M-P to the nominal back-pointer address and then round down to
* align on a P boundary, we will use at most M-P bytes of padding, and at
* least P (M > P => M >= 2P; M == 2P gives the least padding, P). So if we
* use P bytes of padding, then we will overallocate a by P+M-1 bytes, as we
* also add M-1 to the estimated size in case malloc returns an odd pointer.
* a->limit must include this overestimation to satisfy a->avail in [a->base,
* a->limit].
*
* Similarly, if pool->mask is less than POINTER_MASK, we must include enough
* space in the header size to align the back-pointer on a P boundary so that
* it can be found by subtracting P from a->base. This means a->base must be
* on a P boundary, even though subsequent allocations from a may be aligned
* on a lesser (M) boundary. Given powers of two M and P as above, the extra
* space needed when P > M is P-M or POINTER_MASK - pool->mask.
*
* The size of a header including padding is given by the HEADER_SIZE macro,
* below, for any pool (for any value of M).
*
* The mask to align a->base for any pool is (pool->mask | POINTER_MASK), or
* HEADER_BASE_MASK(pool).
*
* PTR_TO_HEADER computes the address of the back-pointer, given an oversized
* allocation at p. By definition, p must be a->base for the arena a that
* contains p. GET_HEADER and SET_HEADER operate on an oversized arena a, in
* the case of SET_HEADER with back-pointer ap.
*/
#define POINTER_MASK ((jsuword)(JS_ALIGN_OF_POINTER - 1))
#define HEADER_SIZE(pool) (sizeof(JSArena **) \
+ (((pool)->mask < POINTER_MASK) \
? POINTER_MASK - (pool)->mask \
: (pool)->mask - POINTER_MASK))
#define HEADER_BASE_MASK(pool) ((pool)->mask | POINTER_MASK)
#define PTR_TO_HEADER(pool,p) (JS_ASSERT(((jsuword)(p) \
& HEADER_BASE_MASK(pool)) \
== 0), \
(JSArena ***)(p) - 1)
#define GET_HEADER(pool,a) (*PTR_TO_HEADER(pool, (a)->base))
#define SET_HEADER(pool,a,ap) (*PTR_TO_HEADER(pool, (a)->base) = (ap))
JS_PUBLIC_API(void *)
JS_ArenaAllocate(JSArenaPool *pool, size_t nb)
{
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
JSArena **ap, **bp, *a, *b;
jsuword extra, hdrsz, gross, sz;
void *p;
/* Search pool from current forward till we find or make enough space. */
JS_ASSERT((nb & pool->mask) == 0);
for (a = pool->current; a->avail + nb > a->limit; pool->current = a) {
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
ap = &a->next;
if (!*ap) {
/* Not enough space in pool -- try to reclaim a free arena. */
extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0;
hdrsz = sizeof *a + extra + pool->mask;
gross = hdrsz + JS_MAX(nb, pool->arenasize);
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
bp = &arena_freelist;
JS_ACQUIRE_LOCK(arena_freelist_lock);
while ((b = *bp) != NULL) {
/*
* Insist on exact arenasize match if nb is not greater than
Fixes to make JS GC truly exact: - All jsvals for which JSVAL_IS_GCTHING evaluates to true must contain tagged pointers into the GC heap -- therefore jsapi.c's JS_DefineConstDoubles cannot "cheat" by tagging addresses of static jsdoubles to avoid js_NewNumberValue. - Finalization is now interleaved with the Sweep phase, to avoid allocating memory for finalization records while sweeping. Instead, the JSRuntime holds a preallocated JSGCThing vector (gcFinalVec) that the Sweep phase fills and flushes via gc_finalize_phase, repeatedly. This means that finalizers cannot allocate a new GC thing, an incompatible but plausible change. js_AllocGCThing asserts and then checks whether it is called while rt->gcLevel is non-zero, and fails the allocation attempt if so. But this fixes bug 38942, where the old sweep-then-finalize with a sweep => malloc dependency could lead to memory exhaustion. - Instead of scanning whole stackPool arenas, which led to UMRs (bug 27924) and sometimes to gross over-scanning that depended on the GC bounds-checking all thing pointers against its heap, we scan exactly those stack slots in use: - arguments reachable from fp->argv; - variables reachable from fp->vars; - operands now reachable from fp->spbase, bounded above by the lesser of fp->sp or fp->spbase + fp->script->depth for an interpreted frame; if the latter, fp->sp has advanced logically above the operand budget, in order to call a native method, and all unused slots from fp->sp up to depth slots above fp->spbase must be set to JSVAL_VOID; - stack segments pushed when calling native methods, prefixed by JSStackHeader structs and linked from cx->stackSegments through each header. The stack segment headers help the GC avoid scanning unused portions of the stack: the generating pc slots running depth slots below fp->spbase, and slots at the end of an arena that aren't sufficient to satisfy a contiguous allocation for more args, vars, or operands. - Exact GC means the stack pointer must remain above live operands until the interpreter is done with them, so jsinterp.c got heavily whacked. Instead of POPs of various kinds followed by a PUSH for binary operators (e.g.), we use FETCH and STORE macros that index by -1 and -2 from sp, and minimize adjustments to sp. When sp is homed to fp->sp, this allows js_DecompileValueGenerator to find the value reliably, and if possible its generating pc. - Finally, the O(n**2) growth rate of gc_find_flags has been fixed, using the scheme sketched in bug 49816 and documented in a new major comment in jsgc.c. Briefly, by allocating flags and things from one arena, we can align things on 1024-byte "thing page" boundaries, and use JSGCPageInfo headers in each page to find a given thing's flags in O(1) time. /be
2000-09-14 10:14:45 +04:00
* arenasize. Otherwise take any arena big enough, but not by
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
* more than gross + arenasize.
*/
sz = JS_UPTRDIFF(b->limit, b);
if (extra
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
? sz >= gross && sz <= gross + pool->arenasize
: sz == gross) {
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
*bp = b->next;
JS_RELEASE_LOCK(arena_freelist_lock);
b->next = NULL;
COUNT(pool, nreclaims);
goto claim;
}
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
bp = &b->next;
}
/* Nothing big enough on the freelist, so we must malloc. */
JS_RELEASE_LOCK(arena_freelist_lock);
b = (JSArena *) malloc(gross);
if (!b)
return 0;
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
b->next = NULL;
b->limit = (jsuword)b + gross;
JS_COUNT_ARENA(pool,++);
COUNT(pool, nmallocs);
claim:
/* If oversized, store ap in the header, just before a->base. */
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
*ap = a = b;
JS_ASSERT(gross <= JS_UPTRDIFF(a->limit, a));
if (extra) {
a->base = a->avail =
((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool);
SET_HEADER(pool, a, ap);
} else {
a->base = a->avail = JS_ARENA_ALIGN(pool, a + 1);
}
continue;
}
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
a = *ap; /* move to next arena */
}
p = (void *)a->avail;
a->avail += nb;
JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);
return p;
}
JS_PUBLIC_API(void *)
JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr)
{
JSArena **ap, *a, *b;
jsuword boff, aoff, extra, hdrsz, gross;
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
/*
* Use the oversized-single-allocation header to avoid searching for ap.
* See JS_ArenaAllocate, the SET_HEADER call.
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
*/
if (size > pool->arenasize) {
ap = *PTR_TO_HEADER(pool, p);
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
a = *ap;
} else {
ap = &pool->first.next;
while ((a = *ap) != pool->current)
ap = &a->next;
}
JS_ASSERT(a->base == (jsuword)p);
boff = JS_UPTRDIFF(a->base, a);
aoff = size + incr;
JS_ASSERT(aoff > pool->arenasize);
extra = HEADER_SIZE(pool); /* oversized header holds ap */
hdrsz = sizeof *a + extra + pool->mask; /* header and alignment slop */
gross = hdrsz + aoff;
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
a = (JSArena *) realloc(a, gross);
if (!a)
return NULL;
#ifdef JS_ARENAMETER
pool->stats.nreallocs++;
#endif
if (a != *ap) {
/* Oops, realloc moved the allocation: update other pointers to a. */
if (pool->current == *ap)
pool->current = a;
b = a->next;
if (b && b->avail - b->base > pool->arenasize) {
JS_ASSERT(GET_HEADER(pool, b) == &(*ap)->next);
SET_HEADER(pool, b, &a->next);
}
/* Now update *ap, the next link of the arena before a. */
*ap = a;
}
a->base = ((jsuword)a + hdrsz) & ~HEADER_BASE_MASK(pool);
a->limit = (jsuword)a + gross;
a->avail = JS_ARENA_ALIGN(pool, a->base + aoff);
JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);
/* Check whether realloc aligned differently, and copy if necessary. */
if (boff != JS_UPTRDIFF(a->base, a))
memmove((void *)a->base, (char *)a + boff, size);
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
/* Store ap in the oversized-load arena header. */
SET_HEADER(pool, a, ap);
return (void *)a->base;
}
JS_PUBLIC_API(void *)
JS_ArenaGrow(JSArenaPool *pool, void *p, size_t size, size_t incr)
{
void *newp;
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
/*
* If p points to an oversized allocation, it owns an entire arena, so we
* can simply realloc the arena.
*/
if (size > pool->arenasize)
return JS_ArenaRealloc(pool, p, size, incr);
JS_ARENA_ALLOCATE(newp, pool, size + incr);
if (newp)
memcpy(newp, p, size);
return newp;
}
/*
* Free tail arenas linked after head, which may not be the true list head.
* Reset pool->current to point to head in case it pointed at a tail arena.
*/
static void
FreeArenaList(JSArenaPool *pool, JSArena *head, JSBool reallyFree)
{
JSArena **ap, *a;
ap = &head->next;
a = *ap;
if (!a)
return;
#ifdef DEBUG
do {
JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);
a->avail = a->base;
JS_CLEAR_UNUSED(a);
} while ((a = a->next) != NULL);
a = *ap;
#endif
if (reallyFree) {
do {
*ap = a->next;
JS_CLEAR_ARENA(a);
JS_COUNT_ARENA(pool,--);
free(a);
} while ((a = *ap) != NULL);
} else {
/* Insert the whole arena chain at the front of the freelist. */
do {
ap = &(*ap)->next;
} while (*ap);
JS_ACQUIRE_LOCK(arena_freelist_lock);
*ap = arena_freelist;
arena_freelist = a;
JS_RELEASE_LOCK(arena_freelist_lock);
head->next = NULL;
}
pool->current = head;
}
JS_PUBLIC_API(void)
JS_ArenaRelease(JSArenaPool *pool, char *mark)
{
JSArena *a;
for (a = &pool->first; a; a = a->next) {
JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);
if (JS_UPTRDIFF(mark, a->base) <= JS_UPTRDIFF(a->avail, a->base)) {
a->avail = JS_ARENA_ALIGN(pool, mark);
JS_ASSERT(a->avail <= a->limit);
FreeArenaList(pool, a, JS_TRUE);
return;
}
}
}
JS_PUBLIC_API(void)
JS_ArenaFreeAllocation(JSArenaPool *pool, void *p, size_t size)
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
{
JSArena **ap, *a, *b;
jsuword q;
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
/*
* If the allocation is oversized, it consumes an entire arena, and it has
* a header just before the allocation pointing back to its predecessor's
* next member. Otherwise, we have to search pool for a.
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
*/
if (size > pool->arenasize) {
ap = *PTR_TO_HEADER(pool, p);
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
a = *ap;
} else {
q = (jsuword)p + size;
q = JS_ARENA_ALIGN(pool, q);
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
ap = &pool->first.next;
while ((a = *ap) != NULL) {
JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
if (a->avail == q) {
/*
* If a is consumed by the allocation at p, we can free it to
* the malloc heap.
*/
if (a->base == (jsuword)p)
break;
/*
* We can't free a, but we can "retract" its avail cursor --
* whether there are others after it in pool.
*/
a->avail = (jsuword)p;
return;
}
ap = &a->next;
}
}
/*
* At this point, a is doomed, so ensure that pool->current doesn't point
* at it. What's more, force future allocations to scavenge all arenas on
* pool, in case some have free space.
*/
if (pool->current == a)
pool->current = &pool->first;
/*
* This is a non-LIFO deallocation, so take care to fix up a->next's back
* pointer in its header, if a->next is oversized.
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
*/
*ap = b = a->next;
if (b && b->avail - b->base > pool->arenasize) {
JS_ASSERT(GET_HEADER(pool, b) == &a->next);
SET_HEADER(pool, b, ap);
Fixes for bug 80981 (``Need extended jump bytecode to avoid "script too large" errors, etc.''): We now ReportStatementTooLarge only if - a jump offset overflows 32 bits, signed; - there are 2**32 or more span dependencies in a script; - a backpatch chain link is more than (2**30 - 1) bytecodes long; - a source note's distance from the last note, or from script main entry point, is > 0x7fffff bytes. Narrative of the patch, by file: - js.c The js_SrcNoteName array of const char * is now a js_SrcNoteSpec array of "specifiers", structs that include a const char *name member. Also, due to span-dependent jumps at the ends of basic blocks where the decompiler knows the basic block length, but not the jump format, we need an offset operand for SRC_COND, SRC_IF_ELSE, and SRC_WHILE (to tell the distance from the branch bytecode after the condition expression to the span-dependent jump). - jsarena.[ch] JS arenas are used mainly for last-in-first-out allocation with _en masse_ release to the malloc pool (or, optionally, to a private freelist). But the code generator needs to allocate and grow (by doubling, to avoid O(n^2) growth) allocations that hold bytecode, source notes, and span-dependency records. This exception to LIFO allocation works by claiming an entire arena from the pool and realloc'ing it, as soon as the allocation size reaches the pool's default arena size. Call such an allocation a "large single allocation". This patch adds a new arena API, JS_ArenaFreeAllocation, which can be used to free a large single allocation. If called with an allocation that's not a large single allocation, it will nevertheless attempt to retract the arena containing that allocation, if the allocation is last within its arena. Thus JS_ArenaFreeAllocation adds a non-LIFO "free" special case to match the non-LIFO "grow" special case already implemented under JS_ARENA_GROW for large single allocations. The code generator still benefits via this extension to arenas, over purely manual malloc/realloc/free, by virtue of _en masse_ free (JS_ARENA_RELEASE after code generation has completed, successfully or not). To avoid searching for the previous arena, in order to update its next member upon reallocation of the arena containing a large single allocation, the oversized arena has a back-pointer to that next member stored (but not as allocable space within the arena) in a (JSArena **) footer at its end. - jscntxt.c I've observed for many scripts that the bytes of source notes and bytecode are of comparable lengths, but only now am I fixing the default arena size for cx->notePool to match the size for cx->codePool (1024 instead of 256). - jsemit.c Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP) and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided into unconditional (gotos and gosubs), and conditional jumps or branches (which pop a value, test it, and jump depending on its value). Most jumps have just one immediate operand, a signed offset from the jump opcode's pc to the target bytecode. The lookup and table switch opcodes may contain many jump offsets. This patch adds "X" counterparts to the opcodes/formats (X is suffixed, btw, to prefer JSOP_ORX and thereby to avoid colliding on the JSOP_XOR name for the extended form of the JSOP_OR branch opcode). The unextended or short formats have 16-bit signed immediate offset operands, the extended or long formats have 32-bit signed immediates. The span-dependency problem consists of selecting as few long instructions as possible, or about as few -- since jumps can span other jumps, extending one jump may cause another to need to be extended. Most JS scripts are short, so need no extended jumps. We optimize for this case by generating short jumps until we know a long jump is needed. After that point, we keep generating short jumps, but each jump's 16-bit immediate offset operand is actually an unsigned index into cg->spanDeps, an array of JSSpanDep structs. Each struct tells the top offset in the script of the opcode, the "before" offset of the jump (which will be the same as top for simplex jumps, but which will index further into the bytecode array for a non-initial jump offset in a lookup or table switch), the after "offset" adjusted during span-dependent instruction selection (initially the same value as the "before" offset), and the jump target (more below). Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must ensure that all bytecode generated so far can be inspected to discover where the jump offset immediate operands lie within CG_CODE(cg). But the bonus is that we generate span-dependency records sorted by their offsets, so we can binary-search when trying to find a JSSpanDep for a given bytecode offset, or the nearest JSSpanDep at or above a given pc. To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows 65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This tells us that we need to binary-search for the cg->spanDeps entry by the jump opcode's bytecode offset (sd->before). Jump targets need to be maintained in a data structure that lets us look up an already-known target by its address (jumps may have a common target), and that also lets us update the addresses (script-relative, a.k.a. absolute offsets) of targets that come after a jump target (for when a jump below that target needs to be extended). We use an AVL tree, implemented using recursion, but with some tricky optimizations to its height-balancing code (see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html). A final wrinkle: backpatch chains are linked by jump-to-jump offsets with positive sign, even though they link "backward" (i.e., toward lower bytecode address). We don't want to waste space and search time in the AVL tree for such temporary backpatch deltas, so we use a single-bit wildcard scheme to tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known target, or is still awaiting backpatching. Note that backpatch chains would present a problem for BuildSpanDepTable, which inspects bytecode to build cg->spanDeps on demand, when the first short jump offset overflows. To solve this temporary problem, we emit a proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP for branch ops) whose nuses/ndefs counts help keep the stack balanced, but whose opcode format distinguishes its backpatch delta immediate operand from a normal jump offset. The cg->spanDeps array and JSJumpTarget structs are allocated from the cx->tempPool arena-pool. This created a LIFO vs. non-LIFO conflict: there were two places under the TOK_SWITCH case in js_EmitTree that used tempPool to allocate and release a chunk of memory, during whose lifetime JSSpanDep and/or JSJumpTarget structs might also be allocated from tempPool -- the ensuing release would prove disastrous. These bitmap and table temporaries are now allocated from the malloc heap. - jsinterp.c Straightforward cloning and JUMP => JUMPX mutating of the jump and switch format bytecode cases. - jsobj.c Silence warnings about %p used without (void *) casts. - jsopcode.c Massive and scary decompiler whackage to cope with extended jumps, using source note offsets to help find jumps whose format (short or long) can't be discovered from properties of prior instructions in the script. One cute hack here: long || and && expressions are broken up to wrap before the 80th column, with the operator at the end of each non-terminal line. - jsopcode.h, jsopcode.tbl The new extended jump opcodes, formats, and fundamental parameterization macros. Also, more comments. - jsparse.c Random and probably only aesthetic fix to avoid decorating a foo[i]++ or --foo[i] parse tree node with JSOP_SETCALL, wrongly (only foo(i)++ or --foo(i), or the other post- or prefix form operator, should have such an opcode decoration on its parse tree). - jsscript.h Random macro naming sanity: use trailing _ rather than leading _ for macro local variables in order to avoid invading the standard C global namespace.
2001-10-17 07:16:48 +04:00
}
JS_CLEAR_ARENA(a);
JS_COUNT_ARENA(pool,--);
free(a);
}
JS_PUBLIC_API(void)
JS_FreeArenaPool(JSArenaPool *pool)
{
FreeArenaList(pool, &pool->first, JS_FALSE);
COUNT(pool, ndeallocs);
}
JS_PUBLIC_API(void)
JS_FinishArenaPool(JSArenaPool *pool)
{
FreeArenaList(pool, &pool->first, JS_TRUE);
#ifdef JS_ARENAMETER
{
JSArenaStats *stats, **statsp;
if (pool->stats.name)
free(pool->stats.name);
for (statsp = &arena_stats_list; (stats = *statsp) != 0;
statsp = &stats->next) {
if (stats == &pool->stats) {
*statsp = stats->next;
return;
}
}
}
#endif
}
JS_PUBLIC_API(void)
JS_ArenaFinish()
{
JSArena *a, *next;
JS_ACQUIRE_LOCK(arena_freelist_lock);
a = arena_freelist;
arena_freelist = NULL;
JS_RELEASE_LOCK(arena_freelist_lock);
for (; a; a = next) {
next = a->next;
free(a);
}
}
JS_PUBLIC_API(void)
JS_ArenaShutDown(void)
{
#ifdef JS_THREADSAFE
/* Must come through here once in the process's last thread! */
if (arena_freelist_lock) {
JS_DESTROY_LOCK(arena_freelist_lock);
arena_freelist_lock = NULL;
}
#endif
}
#ifdef JS_ARENAMETER
JS_PUBLIC_API(void)
JS_ArenaCountAllocation(JSArenaPool *pool, size_t nb)
{
pool->stats.nallocs++;
pool->stats.nbytes += nb;
if (nb > pool->stats.maxalloc)
pool->stats.maxalloc = nb;
pool->stats.variance += nb * nb;
}
JS_PUBLIC_API(void)
JS_ArenaCountInplaceGrowth(JSArenaPool *pool, size_t size, size_t incr)
{
pool->stats.ninplace++;
}
JS_PUBLIC_API(void)
JS_ArenaCountGrowth(JSArenaPool *pool, size_t size, size_t incr)
{
pool->stats.ngrows++;
pool->stats.nbytes += incr;
pool->stats.variance -= size * size;
size += incr;
if (size > pool->stats.maxalloc)
pool->stats.maxalloc = size;
pool->stats.variance += size * size;
}
JS_PUBLIC_API(void)
JS_ArenaCountRelease(JSArenaPool *pool, char *mark)
{
pool->stats.nreleases++;
}
JS_PUBLIC_API(void)
JS_ArenaCountRetract(JSArenaPool *pool, char *mark)
{
pool->stats.nfastrels++;
}
#include <math.h>
#include <stdio.h>
JS_PUBLIC_API(void)
JS_DumpArenaStats(FILE *fp)
{
JSArenaStats *stats;
uint32 nallocs, nbytes;
double mean, variance, sigma;
for (stats = arena_stats_list; stats; stats = stats->next) {
nallocs = stats->nallocs;
if (nallocs != 0) {
nbytes = stats->nbytes;
mean = (double)nbytes / nallocs;
variance = stats->variance * nallocs - nbytes * nbytes;
if (variance < 0 || nallocs == 1)
variance = 0;
else
variance /= nallocs * (nallocs - 1);
sigma = sqrt(variance);
} else {
mean = variance = sigma = 0;
}
fprintf(fp, "\n%s allocation statistics:\n", stats->name);
fprintf(fp, " number of arenas: %u\n", stats->narenas);
fprintf(fp, " number of allocations: %u\n", stats->nallocs);
fprintf(fp, " number of free arena reclaims: %u\n", stats->nreclaims);
fprintf(fp, " number of malloc calls: %u\n", stats->nmallocs);
fprintf(fp, " number of deallocations: %u\n", stats->ndeallocs);
fprintf(fp, " number of allocation growths: %u\n", stats->ngrows);
fprintf(fp, " number of in-place growths: %u\n", stats->ninplace);
fprintf(fp, " number of realloc'ing growths: %u\n", stats->nreallocs);
fprintf(fp, "number of released allocations: %u\n", stats->nreleases);
fprintf(fp, " number of fast releases: %u\n", stats->nfastrels);
fprintf(fp, " total bytes allocated: %u\n", stats->nbytes);
fprintf(fp, " mean allocation size: %g\n", mean);
fprintf(fp, " standard deviation: %g\n", sigma);
fprintf(fp, " maximum allocation size: %u\n", stats->maxalloc);
}
}
#endif /* JS_ARENAMETER */