Browse Source

Merge pull request #1597 from svaarala/newarr-init-size

Add init size for NEWARR opcode (array literal)
pull/1599/head
Sami Vaarala 7 years ago
committed by GitHub
parent
commit
c47ef78c9f
  1. 3
      RELEASES.rst
  2. 1
      debugger/duk_opcodes.yaml
  3. 7
      src-input/duk_hobject.h
  4. 28
      src-input/duk_hobject_props.c
  5. 21
      src-input/duk_js_compiler.c
  6. 35
      src-input/duk_js_executor.c
  7. 56
      tests/ecmascript/test-dev-array-literal-sizes.js
  8. 56
      tests/ecmascript/test-dev-object-literal-sizes.js
  9. 40
      tests/perf/test-array-literal-100.js
  10. 0
      tests/perf/test-array-literal-20.js
  11. 21
      tests/perf/test-array-literal-3.js

3
RELEASES.rst

@ -2982,7 +2982,8 @@ Planned
NOINLINE helpers (GH-1510); duk_harray fast path for internal array
unpack for bound functions, .call, and .apply (GH-1525); unsafe internal
value stack pops where safe (GH-1583, GH-1584); initial object property
table size estimate for NEWOBJ opcode (object literals) (GH-1596)
table size estimates for NEWOBJ and NEWARR opcodes (object and array
literals) (GH-1596, GH-1597)
3.0.0 (XXXX-XX-XX)
------------------

1
debugger/duk_opcodes.yaml

@ -930,6 +930,7 @@ opcodes:
- BC_R
- name: NEWARR
args:
- A_I # array item count init size
- BC_R
- name: MPUTOBJ
args:

7
src-input/duk_hobject.h

@ -868,9 +868,14 @@ DUK_INTERNAL_DECL void duk_hobject_realloc_props(duk_hthread *thr,
duk_uint32_t new_a_size,
duk_uint32_t new_h_size,
duk_bool_t abandon_array);
DUK_INTERNAL_DECL void duk_hobject_resize_props(duk_hthread *thr,
DUK_INTERNAL_DECL void duk_hobject_resize_entrypart(duk_hthread *thr,
duk_hobject *obj,
duk_uint32_t new_e_size);
#if 0 /*unused*/
DUK_INTERNAL_DECL void duk_hobject_resize_arraypart(duk_hthread *thr,
duk_hobject *obj,
duk_uint32_t new_a_size);
#endif
/* low-level property functions */
DUK_INTERNAL_DECL void duk_hobject_find_existing_entry(duk_heap *heap, duk_hobject *obj, duk_hstring *key, duk_int_t *e_idx, duk_int_t *h_idx);

28
src-input/duk_hobject_props.c

@ -842,7 +842,6 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr,
DUK_ASSERT(new_h != NULL);
/* fill new_h with u32 0xff = UNUSED */
DUK_ASSERT(DUK_HOBJECT_GET_PROPS(thr->heap, obj) != NULL);
DUK_ASSERT(new_h_size > 0);
DUK_MEMSET(new_h, 0xff, sizeof(duk_uint32_t) * new_h_size);
@ -960,7 +959,7 @@ DUK_INTERNAL void duk_hobject_realloc_props(duk_hthread *thr,
* Helpers to resize properties allocation on specific needs.
*/
DUK_INTERNAL void duk_hobject_resize_props(duk_hthread *thr,
DUK_INTERNAL void duk_hobject_resize_entrypart(duk_hthread *thr,
duk_hobject *obj,
duk_uint32_t new_e_size) {
duk_uint32_t old_e_size;
@ -984,6 +983,31 @@ DUK_INTERNAL void duk_hobject_resize_props(duk_hthread *thr,
duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}
#if 0 /*unused */
DUK_INTERNAL void duk_hobject_resize_arraypart(duk_hthread *thr,
duk_hobject *obj,
duk_uint32_t new_a_size) {
duk_uint32_t old_a_size;
duk_uint32_t new_e_size;
duk_uint32_t new_h_size;
DUK_ASSERT(thr != NULL);
DUK_ASSERT(obj != NULL);
if (!DUK_HOBJECT_HAS_ARRAY_PART(obj)) {
return;
}
old_a_size = DUK_HOBJECT_GET_ASIZE(obj);
if (old_a_size > new_a_size) {
new_a_size = old_a_size;
}
new_e_size = DUK_HOBJECT_GET_ESIZE(obj);
new_h_size = DUK_HOBJECT_GET_HSIZE(obj);
duk_hobject_realloc_props(thr, obj, new_e_size, new_a_size, new_h_size, 0);
}
#endif
/* Grow entry part allocation for one additional entry. */
DUK_LOCAL void duk__grow_props_for_new_entry_item(duk_hthread *thr, duk_hobject *obj) {
duk_uint32_t old_e_used; /* actually used, non-NULL entries */

21
src-input/duk_js_compiler.c

@ -2818,6 +2818,10 @@ DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re
duk_uarridx_t start_idx; /* start array index of current MPUTARR set */
duk_uarridx_t init_idx; /* last array index explicitly initialized, +1 */
duk_bool_t require_comma; /* next loop requires a comma */
#if !defined(DUK_USE_PREFER_SIZE)
duk_int_t pc_newarr;
duk_compiler_instr *instr;
#endif
/* DUK_TOK_LBRACKET already eaten, current token is right after that */
DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LBRACKET);
@ -2825,6 +2829,9 @@ DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re
max_init_values = DUK__MAX_ARRAY_INIT_VALUES; /* XXX: depend on available temps? */
reg_obj = DUK__ALLOCTEMP(comp_ctx);
#if !defined(DUK_USE_PREFER_SIZE)
pc_newarr = duk__get_current_pc(comp_ctx);
#endif
duk__emit_bc(comp_ctx, DUK_OP_NEWARR, reg_obj); /* XXX: patch initial size hint afterwards? */
temp_start = DUK__GETTEMP(comp_ctx);
@ -2924,6 +2931,14 @@ DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *re
}
}
/* Update initil size for NEWARR, doesn't need to be exact and is
* capped at A field limit.
*/
#if !defined(DUK_USE_PREFER_SIZE)
instr = duk__get_instr_ptr(comp_ctx, pc_newarr);
instr->ins |= DUK_ENC_OP_A(0, curr_idx > DUK_BC_A_MAX ? DUK_BC_A_MAX : curr_idx);
#endif
DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RBRACKET);
duk__advance(comp_ctx);
@ -3008,8 +3023,8 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r
duk_small_uint_t max_init_pairs; /* max # of key-value pairs initialized in one MPUTOBJ set */
duk_bool_t first; /* first value: comma must not precede the value */
duk_bool_t is_set, is_get; /* temps */
duk_int_t pc_newobj;
#if !defined(DUK_USE_PREFER_SIZE)
duk_int_t pc_newobj;
duk_compiler_instr *instr;
#endif
@ -3022,7 +3037,9 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r
st.num_pairs = 0; /* number of key/value pairs emitted for current MPUTOBJ set */
st.num_total_pairs = 0; /* number of key/value pairs emitted overall */
#if !defined(DUK_USE_PREFER_SIZE)
pc_newobj = duk__get_current_pc(comp_ctx);
#endif
duk__emit_bc(comp_ctx, DUK_OP_NEWOBJ, st.reg_obj);
/*
@ -3219,7 +3236,7 @@ DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *r
*/
#if !defined(DUK_USE_PREFER_SIZE)
instr = duk__get_instr_ptr(comp_ctx, pc_newobj);
instr->ins |= DUK_ENC_OP_A(0, st.num_total_pairs > 255 ? 255 : st.num_total_pairs);
instr->ins |= DUK_ENC_OP_A(0, st.num_total_pairs > DUK_BC_A_MAX ? DUK_BC_A_MAX : st.num_total_pairs);
#endif
DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY);

35
src-input/duk_js_executor.c

@ -4741,8 +4741,19 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *
case DUK_OP_NEWOBJ: {
duk_context *ctx = (duk_context *) thr;
duk_push_object(ctx);
#if defined(DUK_USE_ASSERTIONS)
{
duk_hobject *h;
h = duk_require_hobject(ctx, -1);
DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0);
DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0);
DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0);
DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0);
}
#endif
#if !defined(DUK_USE_PREFER_SIZE)
duk_hobject_resize_props(thr, duk_known_hobject(ctx, -1), DUK_DEC_A(ins));
/* XXX: could do a direct props realloc, but need hash size */
duk_hobject_resize_entrypart(thr, duk_known_hobject(ctx, -1), DUK_DEC_A(ins));
#endif
DUK__REPLACE_TOP_BC_BREAK();
}
@ -4750,6 +4761,28 @@ DUK_LOCAL DUK_NOINLINE DUK_HOT void duk__js_execute_bytecode_inner(duk_hthread *
case DUK_OP_NEWARR: {
duk_context *ctx = (duk_context *) thr;
duk_push_array(ctx);
#if defined(DUK_USE_ASSERTIONS)
{
duk_hobject *h;
h = duk_require_hobject(ctx, -1);
DUK_ASSERT(DUK_HOBJECT_GET_ESIZE(h) == 0);
DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h) == 0);
DUK_ASSERT(DUK_HOBJECT_GET_ASIZE(h) == 0);
DUK_ASSERT(DUK_HOBJECT_GET_HSIZE(h) == 0);
DUK_ASSERT(DUK_HOBJECT_HAS_ARRAY_PART(h));
}
#endif
#if !defined(DUK_USE_PREFER_SIZE)
duk_hobject_realloc_props(thr,
duk_known_hobject(ctx, -1),
0 /*new_e_size*/,
DUK_DEC_A(ins) /*new_a_size*/,
0 /*new_h_size*/,
0 /*abandon_array*/);
#if 0
duk_hobject_resize_arraypart(thr, duk_known_hobject(ctx, -1), DUK_DEC_A(ins));
#endif
#endif
DUK__REPLACE_TOP_BC_BREAK();
}

56
tests/ecmascript/test-dev-array-literal-sizes.js

@ -0,0 +1,56 @@
/*
* Array literals of different sizes
*/
/*===
0
100
200
300
400
500
600
700
800
900
done
===*/
function makeTestFunction(count) {
var src = [];
var i;
src.push('(function () {');
src.push(' var arr = [');
for (i = 0; i < count; i++) {
src.push(' "val' + i + '",');
}
src.push(' ];');
src.push(' return arr;');
src.push('})');
return eval(src.join('\n') + '\n');
}
function test() {
var i, j, arr, fn;
for (i = 0; i < 1000; i++) {
if ((i % 100) == 0) {
print(i);
}
fn = makeTestFunction(i);
for (j = 0; j < 100; j++) {
arr = fn();
}
if (Object.keys(arr).length !== i) {
throw new Error('failed for i: ' + i);
}
}
print('done');
}
try {
test();
} catch (e) {
print(e.stack || e);
}

56
tests/ecmascript/test-dev-object-literal-sizes.js

@ -0,0 +1,56 @@
/*
* Object literals of different sizes
*/
/*===
0
100
200
300
400
500
600
700
800
900
done
===*/
function makeTestFunction(count) {
var src = [];
var i;
src.push('(function () {');
src.push(' var obj = {');
for (i = 0; i < count; i++) {
src.push(' key' + i + ': "val' + i + '",');
}
src.push(' };');
src.push(' return obj;');
src.push('})');
return eval(src.join('\n') + '\n');
}
function test() {
var i, j, obj, fn;
for (i = 0; i < 1000; i++) {
if ((i % 100) == 0) {
print(i);
}
fn = makeTestFunction(i);
for (j = 0; j < 100; j++) {
obj = fn();
}
if (Object.keys(obj).length !== i) {
throw new Error('failed for i: ' + i);
}
}
print('done');
}
try {
test();
} catch (e) {
print(e.stack || e);
}

40
tests/perf/test-array-literal-100.js

@ -0,0 +1,40 @@
/*
* Create Array using a literal
*/
if (typeof print !== 'function') { print = console.log; }
function test() {
var arr;
var i;
for (i = 0; i < 1e6; i++) {
arr = [ 'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5',
'foo', 'bar', 'quux', 'baz', 'quuux',
'1', '2', '3', '4', '5' ];
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}

0
tests/perf/test-array-literal.js → tests/perf/test-array-literal-20.js

21
tests/perf/test-array-literal-3.js

@ -0,0 +1,21 @@
/*
* Create Array using a literal
*/
if (typeof print !== 'function') { print = console.log; }
function test() {
var arr;
var i;
for (i = 0; i < 1e6; i++) {
arr = [ 'foo', 'bar', 'quux' ];
}
}
try {
test();
} catch (e) {
print(e.stack || e);
throw e;
}
Loading…
Cancel
Save