Skip to content

fix: Fix growing factor for capacity in Array #1841

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 3, 2021
31 changes: 19 additions & 12 deletions std/assembly/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ import { joinBooleanArray, joinIntegerArray, joinFloatArray, joinStringArray, jo
import { idof, isArray as builtin_isArray } from "./builtins";
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_ILLEGALGENTYPE, E_EMPTYARRAY, E_HOLEYARRAY } from "./util/error";

// @ts-ignore: decorator
@inline @lazy const MIN_SIZE: usize = 8;

/** Ensures that the given array has _at least_ the specified backing size. */
function ensureSize(array: usize, minSize: usize, alignLog2: u32): void {
// depends on the fact that Arrays mimic ArrayBufferView
var oldCapacity = changetype<ArrayBufferView>(array).byteLength;
if (minSize > <usize>oldCapacity >>> alignLog2) {
if (minSize > BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
function ensureCapacity(array: usize, newSize: usize, alignLog2: u32, canGrow: bool = true): void {
// Depends on the fact that Arrays mimic ArrayBufferView
var oldCapacity = <usize>changetype<ArrayBufferView>(array).byteLength;
if (newSize > oldCapacity >>> alignLog2) {
if (newSize > BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
let oldData = changetype<usize>(changetype<ArrayBufferView>(array).buffer);
let newCapacity = minSize << alignLog2;
// Grows old capacity by factor of two.
// Make sure we don't reach BLOCK_MAXSIZE for new growed capacity.
let newCapacity = max(newSize, MIN_SIZE) << alignLog2;
if (canGrow) newCapacity = max(min(oldCapacity << 1, BLOCK_MAXSIZE), newCapacity);
let newData = __renew(oldData, newCapacity);
memory.fill(newData + oldCapacity, 0, newCapacity - oldCapacity);
if (newData !== oldData) { // oldData has been free'd
store<usize>(array, newData, offsetof<ArrayBufferView>("buffer"));
store<usize>(array, newData, offsetof<ArrayBufferView>("dataStart"));
__link(array, changetype<usize>(newData), false);
}
store<u32>(array, newCapacity, offsetof<ArrayBufferView>("byteLength"));
store<u32>(array, <u32>newCapacity, offsetof<ArrayBufferView>("byteLength"));
}
}

Expand Down Expand Up @@ -56,7 +62,8 @@ export class Array<T> {

constructor(length: i32 = 0) {
if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignof<T>()) throw new RangeError(E_INVALIDLENGTH);
var bufferSize = <usize>length << alignof<T>();
// reserve capacity for at least MIN_SIZE elements
var bufferSize = <usize>max(length, MIN_SIZE) << alignof<T>();
var buffer = changetype<ArrayBuffer>(__new(bufferSize, idof<ArrayBuffer>()));
memory.fill(changetype<usize>(buffer), 0, bufferSize);
this.buffer = buffer; // links
Expand All @@ -70,7 +77,7 @@ export class Array<T> {
}

set length(newLength: i32) {
ensureSize(changetype<usize>(this), newLength, alignof<T>());
ensureCapacity(changetype<usize>(this), newLength, alignof<T>(), false);
this.length_ = newLength;
}

Expand Down Expand Up @@ -106,7 +113,7 @@ export class Array<T> {
@operator("[]=") private __set(index: i32, value: T): void {
if (<u32>index >= <u32>this.length_) {
if (index < 0) throw new RangeError(E_INDEXOUTOFRANGE);
ensureSize(changetype<usize>(this), index + 1, alignof<T>());
ensureCapacity(changetype<usize>(this), index + 1, alignof<T>());
this.length_ = index + 1;
}
this.__uset(index, value);
Expand Down Expand Up @@ -204,7 +211,7 @@ export class Array<T> {
push(value: T): i32 {
var length = this.length_;
var newLength = length + 1;
ensureSize(changetype<usize>(this), newLength, alignof<T>());
ensureCapacity(changetype<usize>(this), newLength, alignof<T>());
if (isManaged<T>()) {
store<usize>(this.dataStart + (<usize>length << alignof<T>()), changetype<usize>(value));
__link(changetype<usize>(this), changetype<usize>(value), true);
Expand Down Expand Up @@ -353,7 +360,7 @@ export class Array<T> {

unshift(value: T): i32 {
var newLength = this.length_ + 1;
ensureSize(changetype<usize>(this), newLength, alignof<T>());
ensureCapacity(changetype<usize>(this), newLength, alignof<T>());
var dataStart = this.dataStart;
memory.copy(
dataStart + sizeof<T>(),
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/assert-nonnull.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
if
i32.const 1184
i32.const 1248
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -212,7 +212,7 @@
if
i32.const 1184
i32.const 1248
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand All @@ -228,7 +228,7 @@
if
i32.const 1296
i32.const 1248
i32.const 96
i32.const 103
i32.const 40
call $~lib/builtins/abort
unreachable
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/assert-nonnull.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@
if
i32.const 160
i32.const 224
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand All @@ -334,7 +334,7 @@
if
i32.const 272
i32.const 224
i32.const 96
i32.const 103
i32.const 40
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -365,7 +365,7 @@
if
i32.const 160
i32.const 224
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/class.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -1820,13 +1820,13 @@
i32.const 0
i32.store offset=12
global.get $~lib/memory/__stack_pointer
i32.const 0
i32.const 32
i32.const 0
call $~lib/rt/itcms/__new
local.tee $1
i32.store offset=4
local.get $1
i32.const 0
i32.const 32
call $~lib/memory/memory.fill
local.get $0
local.get $1
Expand All @@ -1835,7 +1835,7 @@
local.get $1
i32.store offset=4
local.get $0
i32.const 0
i32.const 32
i32.store offset=8
local.get $0
i32.const 0
Expand Down
31 changes: 20 additions & 11 deletions tests/compiler/class.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,8 @@
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
global.get $~lib/memory/__stack_pointer
i32.const 8
i32.sub
Expand Down Expand Up @@ -2721,44 +2723,51 @@
if
i32.const 432
i32.const 480
i32.const 58
i32.const 64
i32.const 60
call $~lib/builtins/abort
unreachable
end
local.get $1
local.tee $2
i32.const 8
local.tee $3
local.get $2
local.get $3
i32.gt_s
select
i32.const 2
i32.shl
local.set $2
local.set $4
global.get $~lib/memory/__stack_pointer
local.get $2
local.get $4
i32.const 0
call $~lib/rt/itcms/__new
local.tee $3
local.tee $5
i32.store offset=4
local.get $3
local.get $5
i32.const 0
local.get $2
local.get $4
call $~lib/memory/memory.fill
local.get $0
local.get $3
local.get $5
call $~lib/array/Array<i32>#set:buffer
local.get $0
local.get $3
local.get $5
call $~lib/array/Array<i32>#set:dataStart
local.get $0
local.get $2
local.get $4
call $~lib/array/Array<i32>#set:byteLength
local.get $0
local.get $1
call $~lib/array/Array<i32>#set:length_
local.get $0
local.set $4
local.set $6
global.get $~lib/memory/__stack_pointer
i32.const 8
i32.add
global.set $~lib/memory/__stack_pointer
local.get $4
local.get $6
)
(func $class/GenericInitializer<i32>#constructor (param $0 i32) (result i32)
(local $1 i32)
Expand Down
59 changes: 39 additions & 20 deletions tests/compiler/extends-baseaggregate.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -2150,15 +2150,14 @@
i32.store offset=4
i32.const 1180
i32.load
local.tee $9
local.tee $8
i32.const 1
i32.add
local.tee $5
local.set $1
local.get $5
local.tee $9
local.tee $1
i32.const 1176
i32.load
local.tee $6
local.tee $4
i32.const 2
i32.shr_u
i32.gt_u
Expand All @@ -2169,45 +2168,65 @@
if
i32.const 1616
i32.const 1664
i32.const 14
i32.const 17
i32.const 48
call $~lib/builtins/abort
unreachable
end
i32.const 1168
i32.load
local.tee $7
local.tee $6
local.set $0
block $__inlined_func$~lib/rt/itcms/__renew
local.get $4
i32.const 1
i32.shl
local.tee $3
i32.const 1073741820
local.get $3
i32.const 1073741820
i32.lt_u
select
local.tee $3
local.get $1
i32.const 8
local.get $1
i32.const 8
i32.gt_u
select
i32.const 2
i32.shl
local.tee $8
local.tee $1
local.get $1
local.get $3
i32.lt_u
select
local.tee $7
local.tee $3
local.get $7
local.get $6
i32.const 20
i32.sub
local.tee $4
local.tee $5
i32.load
i32.const -4
i32.and
i32.const 16
i32.sub
i32.le_u
if
local.get $4
local.get $5
local.get $3
i32.store offset=16
br $__inlined_func$~lib/rt/itcms/__renew
end
local.get $3
local.get $4
local.get $5
i32.load offset=12
call $~lib/rt/itcms/__new
local.tee $1
local.get $0
local.get $3
local.get $4
local.get $5
i32.load offset=16
local.tee $0
local.get $0
Expand All @@ -2219,14 +2238,14 @@
local.set $0
end
local.get $0
local.get $6
local.get $4
i32.add
local.get $8
local.get $6
local.get $7
local.get $4
i32.sub
call $~lib/memory/memory.fill
local.get $0
local.get $7
local.get $6
i32.ne
if
i32.const 1168
Expand All @@ -2241,12 +2260,12 @@
call $~lib/rt/itcms/__link
end
i32.const 1176
local.get $8
local.get $7
i32.store
end
i32.const 1172
i32.load
local.get $9
local.get $8
i32.const 2
i32.shl
i32.add
Expand All @@ -2257,7 +2276,7 @@
i32.const 1
call $~lib/rt/itcms/__link
i32.const 1180
local.get $5
local.get $9
i32.store
global.get $~lib/memory/__stack_pointer
i32.const 8
Expand Down
Loading