Skip to content

Decouple Array<T> from ArrayBufferView #1121

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 2 commits into from
Mar 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ function postInstantiate(baseModule, instance) {
/** Allocates a new array in the module's memory and returns its retained pointer. */
function __allocArray(id, values) {
const info = getInfo(id);
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + " @ " + info);
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + ", flags= " + info);
const align = getValueAlign(info);
const length = values.length;
const buf = alloc(length << align, ARRAYBUFFER_ID);
Expand All @@ -188,7 +188,7 @@ function postInstantiate(baseModule, instance) {
const U32 = new Uint32Array(memory.buffer);
const id = U32[arr + ID_OFFSET >>> 2];
const info = getInfo(id);
if (!(info & ARRAYBUFFERVIEW)) throw Error("not an array: " + id);
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + ", flags=" + info);
const align = getValueAlign(info);
var buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
const length = info & ARRAY
Expand Down
Binary file modified lib/loader/tests/build/default.wasm
Binary file not shown.
Binary file modified lib/loader/tests/build/legacy.wasm
Binary file not shown.
5 changes: 4 additions & 1 deletion src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7910,7 +7910,10 @@ export function compileRTTI(compiler: Compiler): void {
if (instance !== abvInstance && instance.extends(abvPrototype)) {
let valueType = instance.getArrayValueType();
flags |= TypeinfoFlags.ARRAYBUFFERVIEW;
if (instance.extends(arrayPrototype)) flags |= TypeinfoFlags.ARRAY;
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(valueType);
} else if (instance.extends(arrayPrototype)) {
let valueType = instance.getArrayValueType();
flags |= TypeinfoFlags.ARRAY;
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(valueType);
} else if (instance.extends(setPrototype)) {
let typeArguments = assert(instance.getTypeArgumentsTo(setPrototype));
Expand Down
40 changes: 26 additions & 14 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3751,25 +3751,37 @@ export class Class extends TypedElement {
getArrayValueType(): Type {
var current: Class = this;
var program = this.program;
var arrayPrototype = program.arrayPrototype;
if (this.extends(arrayPrototype)) {
return this.getTypeArgumentsTo(arrayPrototype)![0];
}
var abvInstance = program.arrayBufferViewInstance;
while (current.base !== abvInstance) {
current = assert(current.base);
}
var prototype = current.prototype;
if (prototype == program.arrayPrototype) {
return this.getTypeArgumentsTo(program.arrayPrototype)![0];
}
if (prototype == program.i8ArrayPrototype) return Type.i8;
if (prototype == program.i16ArrayPrototype) return Type.i16;
if (prototype == program.i32ArrayPrototype) return Type.i32;
if (prototype == program.i64ArrayPrototype) return Type.i64;
if (prototype == program.u8ArrayPrototype) return Type.u8;
if (prototype == program.u8ClampedArrayPrototype) return Type.u8;
if (prototype == program.u16ArrayPrototype) return Type.u16;
if (prototype == program.u32ArrayPrototype) return Type.u32;
if (prototype == program.u64ArrayPrototype) return Type.u64;
if (prototype == program.f32ArrayPrototype) return Type.f32;
if (prototype == program.f64ArrayPrototype) return Type.f64;
switch (prototype.name.charCodeAt(0)) {
case CharCode.F: {
if (prototype == program.f32ArrayPrototype) return Type.f32;
if (prototype == program.f64ArrayPrototype) return Type.f64;
break;
}
case CharCode.I: {
if (prototype == program.i8ArrayPrototype) return Type.i8;
if (prototype == program.i16ArrayPrototype) return Type.i16;
if (prototype == program.i32ArrayPrototype) return Type.i32;
if (prototype == program.i64ArrayPrototype) return Type.i64;
break;
}
case CharCode.U: {
if (prototype == program.u8ArrayPrototype) return Type.u8;
if (prototype == program.u8ClampedArrayPrototype) return Type.u8;
if (prototype == program.u16ArrayPrototype) return Type.u16;
if (prototype == program.u32ArrayPrototype) return Type.u32;
if (prototype == program.u64ArrayPrototype) return Type.u64;
break;
}
}
assert(false);
return Type.void;
}
Expand Down
23 changes: 17 additions & 6 deletions std/assembly/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@

import { BLOCK_MAXSIZE } from "./rt/common";
import { COMPARATOR, SORT } from "./util/sort";
import { ArrayBufferView } from "./arraybuffer";
import { joinBooleanArray, joinIntegerArray, joinFloatArray, joinStringArray, joinReferenceArray } from "./util/string";
import { idof, isArray as builtin_isArray } from "./builtins";
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_EMPTYARRAY, E_HOLEYARRAY } from "./util/error";

/** 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);
Expand All @@ -24,12 +24,17 @@ function ensureSize(array: usize, minSize: usize, alignLog2: u32): void {
}
}

export class Array<T> extends ArrayBufferView {
export class Array<T> {
[key: number]: T;

// Implementing ArrayBufferView isn't strictly necessary here but is done to allow glue code
// Mimicking ArrayBufferView isn't strictly necessary here but is done to allow glue code
// to work with typed and normal arrays interchangeably. Technically, normal arrays do not need
// `dataStart` (equals `data`) and `dataLength` (equals computed `data.byteLength`).
// `dataStart` (equals `buffer`) and `byteLength` (equals computed `buffer.byteLength`), but the
// block is 16 bytes anyway so it's fine to have a couple extra fields in there.

private buffer: ArrayBuffer;
private dataStart: usize;
private byteLength: i32;

// Also note that Array<T> with non-nullable T must guard against uninitialized null values
// whenever an element is accessed. Otherwise, the compiler wouldn't be able to guarantee
Expand All @@ -49,7 +54,13 @@ export class Array<T> extends ArrayBufferView {
}

constructor(length: i32 = 0) {
super(length, alignof<T>());
if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignof<T>()) throw new RangeError(E_INVALIDLENGTH);
var bufferSize = <usize>length << alignof<T>();
var buffer = __alloc(bufferSize, idof<ArrayBuffer>());
memory.fill(buffer, 0, bufferSize);
this.buffer = changetype<ArrayBuffer>(buffer); // retains
this.dataStart = buffer;
this.byteLength = <i32>bufferSize;
this.length_ = length;
}

Expand Down Expand Up @@ -498,6 +509,6 @@ export class Array<T> extends ArrayBufferView {
cur += sizeof<usize>();
}
}
// automatically visits ArrayBufferView (.buffer) next
__visit(changetype<usize>(this.buffer), cookie);
}
}
6 changes: 3 additions & 3 deletions tests/compiler/assert-nonnull.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
if
i32.const 1104
i32.const 1168
i32.const 93
i32.const 104
i32.const 41
call $~lib/builtins/abort
unreachable
Expand All @@ -98,7 +98,7 @@
if
i32.const 1216
i32.const 1168
i32.const 97
i32.const 108
i32.const 39
call $~lib/builtins/abort
unreachable
Expand All @@ -113,7 +113,7 @@
if
i32.const 1104
i32.const 1168
i32.const 93
i32.const 104
i32.const 41
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 @@ -122,7 +122,7 @@
if
i32.const 96
i32.const 160
i32.const 93
i32.const 104
i32.const 41
call $~lib/builtins/abort
unreachable
Expand All @@ -138,7 +138,7 @@
call $~lib/rt/stub/__release
i32.const 208
i32.const 160
i32.const 97
i32.const 108
i32.const 39
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -188,7 +188,7 @@
if
i32.const 96
i32.const 160
i32.const 93
i32.const 104
i32.const 41
call $~lib/builtins/abort
unreachable
Expand Down
4 changes: 2 additions & 2 deletions tests/compiler/builtins.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -605,8 +605,8 @@
f64.const 0
f64.const 0
f64.const 12
f64.const 24
f64.const 24
f64.const 25
f64.const 25
call $~lib/builtins/trace
i32.const 1216
i32.const 1216
Expand Down
4 changes: 2 additions & 2 deletions tests/compiler/builtins.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -1563,9 +1563,9 @@
local.set $1
i32.const 12
local.set $6
i32.const 24
i32.const 25
local.set $7
i32.const 24
i32.const 25
local.set $8
i32.const 128
i32.const 5
Expand Down
29 changes: 10 additions & 19 deletions tests/compiler/class.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
(type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
(memory $0 1)
(data (i32.const 1024) "\1c\00\00\00\01\00\00\00\01\00\00\00\1c\00\00\00I\00n\00v\00a\00l\00i\00d\00 \00l\00e\00n\00g\00t\00h")
(data (i32.const 1072) "&\00\00\00\01\00\00\00\01\00\00\00&\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00b\00u\00f\00f\00e\00r\00.\00t\00s")
(data (i32.const 1072) "\1a\00\00\00\01\00\00\00\01\00\00\00\1a\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s")
(global $~lib/rt/stub/startOffset (mut i32) (i32.const 0))
(global $~lib/rt/stub/offset (mut i32) (i32.const 0))
(export "memory" (memory $0))
Expand Down Expand Up @@ -123,23 +123,14 @@
i32.const 4
i32.const 4
call $~lib/rt/stub/__alloc
i32.const 16
i32.const 5
call $~lib/rt/stub/__alloc
local.set $0
i32.const 0
i32.const 0
call $~lib/rt/stub/__alloc
local.set $1
local.get $0
i32.eqz
if
i32.const 12
i32.const 2
call $~lib/rt/stub/__alloc
local.set $0
end
local.get $0
i32.const 16
i32.const 5
call $~lib/rt/stub/__alloc
local.tee $0
i32.const 0
i32.store
local.get $0
Expand All @@ -149,6 +140,9 @@
i32.const 0
i32.store offset=8
local.get $0
i32.const 0
i32.store offset=12
local.get $0
i32.load
drop
local.get $0
Expand All @@ -164,15 +158,12 @@
i32.const 0
i32.store offset=12
local.get $0
i32.const 0
i32.store offset=12
local.get $0
i32.store
)
(func $~start (; 3 ;)
i32.const 1136
i32.const 1120
global.set $~lib/rt/stub/startOffset
i32.const 1136
i32.const 1120
global.set $~lib/rt/stub/offset
)
)
Loading