diff --git a/src/builtins.ts b/src/builtins.ts index 9944c5c8..5c282c29 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -21,7 +21,8 @@ import { LiteralKind, LiteralExpression, StringLiteralExpression, - CallExpression + CallExpression, + ElementAccessExpression } from "./ast"; import { @@ -48,7 +49,10 @@ import { getConstValueI64Low, getConstValueI32, getConstValueF32, - getConstValueF64 + getConstValueF64, + getBinaryOp, + getBinaryLeft, + getBinaryRight } from "./module"; import { @@ -476,6 +480,19 @@ export namespace BuiltinSymbols { export const memory_fill = "~lib/runtime/memory.fill"; export const gc_classId = "~lib/runtime/gc.classId"; export const gc_iterateRoots = "~lib/runtime/gc.iterateRoots"; + + // std/typedarray.ts + export const Int8Array = "~lib/typedarray/Int8Array"; + export const Uint8Array = "~lib/typedarray/Uint8Array"; + export const Int16Array = "~lib/typedarray/Int16Array"; + export const Uint16Array = "~lib/typedarray/Uint16Array"; + export const Int32Array = "~lib/typedarray/Int32Array"; + export const Uint32Array = "~lib/typedarray/Uint32Array"; + export const Int64Array = "~lib/typedarray/Int64Array"; + export const Uint64Array = "~lib/typedarray/Uint64Array"; + export const Uint8ClampedArray = "~lib/typedarray/Uint8ClampedArray"; + export const Float32Array = "~lib/typedarray/Float32Array"; + export const Float64Array = "~lib/typedarray/Float64Array"; } /** Compiles a call to a built-in function. */ @@ -4081,6 +4098,209 @@ export function compileIterateRoots(compiler: Compiler): void { ); } +function typedArraySymbolToType(symbol: string): Type { + switch (symbol) { + default: assert(false); + case BuiltinSymbols.Int8Array: return Type.i8; + case BuiltinSymbols.Uint8ClampedArray: + case BuiltinSymbols.Uint8Array: return Type.u8; + case BuiltinSymbols.Int16Array: return Type.i16; + case BuiltinSymbols.Uint16Array: return Type.u16; + case BuiltinSymbols.Int32Array: return Type.i32; + case BuiltinSymbols.Uint32Array: return Type.u32; + case BuiltinSymbols.Int64Array: return Type.i64; + case BuiltinSymbols.Uint64Array: return Type.u64; + case BuiltinSymbols.Float32Array: return Type.f32; + case BuiltinSymbols.Float64Array: return Type.f64; + } +} + +export function compileTypedArrayGet( + compiler: Compiler, + target: Class, + thisExpression: Expression, + elementExpression: Expression, + contextualType: Type +): ExpressionRef { + var type = typedArraySymbolToType(target.internalName); + var module = compiler.module; + var outType = ( + type.is(TypeFlags.INTEGER) && + contextualType.is(TypeFlags.INTEGER) && + contextualType.size > type.size + ) ? contextualType : type; + + var bufferField = assert(target.lookupInSelf("buffer")); + assert(bufferField.kind == ElementKind.FIELD); + var dataStart = assert(target.lookupInSelf("dataStart")); + assert(dataStart.kind == ElementKind.FIELD); + var dataEnd = assert(target.lookupInSelf("dataEnd")); + assert(dataEnd.kind == ElementKind.FIELD); + + var constantOffset: i32 = 0; + var dynamicOffset = module.precomputeExpression(compiler.compileExpression( + elementExpression, + Type.i32, + ConversionKind.IMPLICIT, + WrapMode.NONE + )); + if (getExpressionId(dynamicOffset) == ExpressionId.Const) { + constantOffset = getConstValueI32(dynamicOffset); + dynamicOffset = 0; + } else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) { + if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) { + let left = getBinaryLeft(dynamicOffset); + let right = getBinaryRight(dynamicOffset); + if (getExpressionId(left) == ExpressionId.Const) { + constantOffset = getConstValueI32(left); + dynamicOffset = right; + } else if (getExpressionId(right) == ExpressionId.Const) { + constantOffset = getConstValueI32(right); + dynamicOffset = left; + } + } + } + + var usizeType = compiler.options.usizeType; + var nativeSizeType = compiler.options.nativeSizeType; + var dataStartExpr = module.createLoad(usizeType.byteSize, true, + compiler.compileExpression( + thisExpression, + target.type, + ConversionKind.IMPLICIT, + WrapMode.NONE + ), + nativeSizeType, (dataStart).memoryOffset + ); + if (dynamicOffset) { + if (nativeSizeType == NativeType.I64) { + dataStartExpr = module.createBinary(BinaryOp.AddI64, + dataStartExpr, + module.createUnary(UnaryOp.ExtendU32, dynamicOffset) + ); + } else { + dataStartExpr = module.createBinary(BinaryOp.AddI32, + dataStartExpr, + dynamicOffset + ); + } + } + + // TODO: check offset + + compiler.currentType = outType; + return module.createLoad( + type.byteSize, + type.is(TypeFlags.SIGNED), + dataStartExpr, + outType.toNativeType(), + constantOffset + ); +} + +export function compileTypedArraySet( + compiler: Compiler, + target: Class, + thisExpression: Expression, + elementExpression: Expression, + valueExpression: Expression, + contextualType: Type +): ExpressionRef { + var type = typedArraySymbolToType(target.internalName); + var module = compiler.module; + + var bufferField = assert(target.lookupInSelf("buffer")); + assert(bufferField.kind == ElementKind.FIELD); + var dataStart = assert(target.lookupInSelf("dataStart")); + assert(dataStart.kind == ElementKind.FIELD); + var dataEnd = assert(target.lookupInSelf("dataEnd")); + assert(dataEnd.kind == ElementKind.FIELD); + + var constantOffset: i32 = 0; + var dynamicOffset = module.precomputeExpression(compiler.compileExpression( + elementExpression, + Type.i32, + ConversionKind.IMPLICIT, + WrapMode.NONE + )); + if (getExpressionId(dynamicOffset) == ExpressionId.Const) { + constantOffset = getConstValueI32(dynamicOffset); + dynamicOffset = 0; + } else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) { + if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) { + let left = getBinaryLeft(dynamicOffset); + let right = getBinaryRight(dynamicOffset); + if (getExpressionId(left) == ExpressionId.Const) { + constantOffset = getConstValueI32(left); + dynamicOffset = right; + } else if (getExpressionId(right) == ExpressionId.Const) { + constantOffset = getConstValueI32(right); + dynamicOffset = left; + } + } + } + + var usizeType = compiler.options.usizeType; + var nativeSizeType = compiler.options.nativeSizeType; + var dataStartExpr = module.createLoad(usizeType.byteSize, true, + compiler.compileExpression( + thisExpression, + target.type, + ConversionKind.IMPLICIT, + WrapMode.NONE + ), + nativeSizeType, (dataStart).memoryOffset + ); + if (dynamicOffset) { + if (nativeSizeType == NativeType.I64) { + dataStartExpr = module.createBinary(BinaryOp.AddI64, + dataStartExpr, + module.createUnary(UnaryOp.ExtendU32, dynamicOffset) + ); + } else { + dataStartExpr = module.createBinary(BinaryOp.AddI32, + dataStartExpr, + dynamicOffset + ); + } + } + + var valueExpr = compiler.compileExpression( + valueExpression, + type.is(TypeFlags.SIGNED) ? Type.i32 : Type.u32, + ConversionKind.IMPLICIT, + WrapMode.NONE + ); + var nativeType = type.toNativeType(); + + // TODO: check offset, clamp + + if (contextualType == Type.void) { + compiler.currentType = Type.void; + return module.createStore( + type.byteSize, + dataStartExpr, + valueExpr, + nativeType, + constantOffset + ); + } else { + let flow = compiler.currentFlow; + let tempLocal = flow.getAndFreeTempLocal(type, false); + compiler.currentType = type; + return module.createBlock(null, [ + module.createStore( + type.byteSize, + dataStartExpr, + module.createTeeLocal(tempLocal.index, valueExpr), + nativeType, + constantOffset + ), + module.createGetLocal(tempLocal.index, nativeType) + ], nativeType); + } +} + /** Ensures that the specified class's GC hook exists and returns its function table index. */ export function ensureGCHook( compiler: Compiler, diff --git a/src/common.ts b/src/common.ts index e2518b28..f46ffff1 100644 --- a/src/common.ts +++ b/src/common.ts @@ -174,6 +174,7 @@ export namespace LibrarySymbols { export const V128 = "V128"; export const String = "String"; export const Array = "Array"; + export const ArrayBufferView = "ArrayBufferView"; export const ArrayBuffer = "ArrayBuffer"; export const Math = "Math"; export const Mathf = "Mathf"; diff --git a/src/compiler.ts b/src/compiler.ts index 5b9dfb21..81209c8d 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -8,7 +8,9 @@ import { compileAbort, compileIterateRoots, ensureGCHook, - BuiltinSymbols + BuiltinSymbols, + compileTypedArrayGet, + compileTypedArraySet } from "./builtins"; import { @@ -4683,7 +4685,21 @@ export class Compiler extends DiagnosticEmitter { break; } case ElementKind.CLASS: { - if (resolver.currentElementExpression) { // indexed access + let elementExpression = resolver.currentElementExpression; + if (elementExpression) { // indexed access + let arrayBufferView = this.program.arrayBufferView; + if (arrayBufferView) { + if ((target).prototype.extends(arrayBufferView.prototype)) { + return compileTypedArraySet( + this, + target, + assert(this.resolver.currentThisExpression), + elementExpression, + valueExpression, + contextualType + ); + } + } let isUnchecked = flow.is(FlowFlags.UNCHECKED_CONTEXT); let indexedSet = (target).lookupOverload(OperatorKind.INDEXED_SET, isUnchecked); if (!indexedSet) { @@ -5886,6 +5902,18 @@ export class Compiler extends DiagnosticEmitter { if (!target) return this.module.createUnreachable(); switch (target.kind) { case ElementKind.CLASS: { + let arrayBufferView = this.program.arrayBufferView; + if (arrayBufferView) { + if ((target).prototype.extends(arrayBufferView.prototype)) { + return compileTypedArrayGet( + this, + target, + expression.expression, + expression.elementExpression, + contextualType + ); + } + } let isUnchecked = this.currentFlow.is(FlowFlags.UNCHECKED_CONTEXT); let indexedGet = (target).lookupOverload(OperatorKind.INDEXED_GET, isUnchecked); if (!indexedGet) { diff --git a/src/program.ts b/src/program.ts index 8da3db04..0d610f8b 100644 --- a/src/program.ts +++ b/src/program.ts @@ -333,6 +333,8 @@ export class Program extends DiagnosticEmitter { // runtime references + /** ArrayBufferView reference. */ + arrayBufferView: Class | null = null; /** ArrayBuffer instance reference. */ arrayBufferInstance: Class | null = null; /** Array prototype reference. */ @@ -770,6 +772,10 @@ export class Program extends DiagnosticEmitter { assert(element.kind == ElementKind.CLASS_PROTOTYPE); this.stringInstance = resolver.resolveClass(element, null); } + if (element = this.lookupGlobal(LibrarySymbols.ArrayBufferView)) { + assert(element.kind == ElementKind.CLASS_PROTOTYPE); + this.arrayBufferView = resolver.resolveClass(element, null); + } if (element = this.lookupGlobal(LibrarySymbols.ArrayBuffer)) { assert(element.kind == ElementKind.CLASS_PROTOTYPE); this.arrayBufferInstance = resolver.resolveClass(element, null); @@ -1145,7 +1151,7 @@ export class Program extends DiagnosticEmitter { (declaration.is(CommonFlags.READONLY) ? DecoratorFlags.INLINE : DecoratorFlags.NONE - ) | DecoratorFlags.LAZY + ) | DecoratorFlags.LAZY | DecoratorFlags.UNSAFE ), declaration ); @@ -1156,7 +1162,7 @@ export class Program extends DiagnosticEmitter { name, parent, declaration, - this.checkDecorators(decorators, DecoratorFlags.NONE) + this.checkDecorators(decorators, DecoratorFlags.UNSAFE) ); if (!parent.addInstance(name, element)) return; } diff --git a/std/assembly/arraybuffer.ts b/std/assembly/arraybuffer.ts index 9014d73b..d1cef3e1 100644 --- a/std/assembly/arraybuffer.ts +++ b/std/assembly/arraybuffer.ts @@ -1,8 +1,8 @@ import { - HEADER_SIZE, - MAX_BLENGTH, - allocateUnsafe -} from "./internal/arraybuffer"; + ALLOC_RAW, + REGISTER, + ArrayBufferBase +} from "./runtime"; import { Uint8ClampedArray, @@ -21,9 +21,7 @@ import { } from "./dataview"; @sealed -export class ArrayBuffer { - - readonly byteLength: i32; // capped to [0, MAX_LENGTH] +export class ArrayBuffer extends ArrayBufferBase { @inline static isView(value: T): bool { if (value === null) return false; @@ -40,28 +38,15 @@ export class ArrayBuffer { return false; } - // @unsafe - @inline get data(): usize { return changetype(this) + HEADER_SIZE; } - - constructor(length: i32, unsafe: bool = false) { - if (length > MAX_BLENGTH) throw new RangeError("Invalid array buffer length"); - var buffer = allocateUnsafe(length); - if (!unsafe) memory.fill(changetype(buffer) + HEADER_SIZE, 0, length); - return buffer; - } - - slice(begin: i32 = 0, end: i32 = MAX_BLENGTH): ArrayBuffer { + slice(begin: i32 = 0, end: i32 = ArrayBuffer.MAX_BYTELENGTH): ArrayBuffer { var len = this.byteLength; begin = begin < 0 ? max(len + begin, 0) : min(begin, len); end = end < 0 ? max(len + end, 0) : min(end, len); len = max(end - begin, 0); - var buffer = allocateUnsafe(len); - memory.copy( - changetype(buffer) + HEADER_SIZE, - changetype(this) + HEADER_SIZE + begin, - len - ); - return buffer; + var outSize = len; + var out = ALLOC_RAW(outSize); + memory.copy(out, changetype(this) + begin, outSize); + return REGISTER(out); } toString(): string { diff --git a/std/assembly/internal/typedarray.ts b/std/assembly/internal/typedarray.ts index 69d4d5b1..db9f7d3f 100644 --- a/std/assembly/internal/typedarray.ts +++ b/std/assembly/internal/typedarray.ts @@ -1,10 +1,12 @@ import { - HEADER_SIZE as AB_HEADER_SIZE, - MAX_BLENGTH as AB_MAX_BLENGTH, - allocateUnsafe, - LOAD, - STORE -} from "./arraybuffer"; + ALLOC, + REGISTER, + LINK +} from "../runtime"; + +import { + ArrayBuffer +} from "../arraybuffer"; import { SORT as SORT_IMPL @@ -19,228 +21,184 @@ export abstract class TypedArray { readonly byteLength: i32; constructor(length: i32) { - const MAX_LENGTH = AB_MAX_BLENGTH / sizeof(); + const MAX_LENGTH = ArrayBuffer.MAX_BYTELENGTH / sizeof(); if (length > MAX_LENGTH) throw new RangeError("Invalid typed array length"); var byteLength = length << alignof(); - var buffer = allocateUnsafe(byteLength); - memory.fill(changetype(buffer) + AB_HEADER_SIZE, 0, byteLength); - this.buffer = buffer; + this.buffer = new ArrayBuffer(byteLength); this.byteOffset = 0; this.byteLength = byteLength; } - @inline get length(): i32 { return this.byteLength >>> alignof(); } - @operator("[]") - protected __get(index: i32): T { + // TODO: could compute load/store offset from index and emit an immediate -> make this a builtin? + + @operator("[]") protected __get(index: i32): T { if (index >= (this.byteLength >>> alignof())) throw new Error("Index out of bounds"); - return LOAD(this.buffer, index, this.byteOffset); + return load(changetype(this.buffer) + this.byteOffset, index << alignof()); } - @inline @operator("{}") - protected __unchecked_get(index: i32): T { - return LOAD(this.buffer, index, this.byteOffset); + @inline @operator("{}") protected __unchecked_get(index: i32): T { + return load(changetype(this.buffer) + this.byteOffset + (index << alignof())); } - @operator("[]=") - protected __set(index: i32, value: native): void { + @operator("[]=") protected __set(index: i32, value: native): void { if (index >= (this.byteLength >>> alignof())) throw new Error("Index out of bounds"); - STORE>(this.buffer, index, value, this.byteOffset); + store(changetype(this.buffer) + this.byteOffset + (index << alignof()), value); } - @inline @operator("{}=") - protected __unchecked_set(index: i32, value: native): void { - STORE>(this.buffer, index, value, this.byteOffset); + @inline @operator("{}=") protected __unchecked_set(index: i32, value: native): void { + store(changetype(this.buffer) + this.byteOffset + (index << alignof()), value); } // copyWithin(target: i32, start: i32, end: i32 = this.length): this } -@inline -export function FILL, T extends number>( +@inline export function FILL, T extends number>( array: TArray, value: native, start: i32, end: i32 ): TArray { - var buffer = array.buffer; - var byteOffset = array.byteOffset; + var base = changetype(array.buffer) + array.byteOffset; var len = array.length; start = start < 0 ? max(len + start, 0) : min(start, len); end = end < 0 ? max(len + end, 0) : min(end, len); if (sizeof() == 1) { - if (start < end) { - memory.fill( - changetype(buffer) + start + byteOffset + AB_HEADER_SIZE, - value, - (end - start) - ); - } + if (start < end) memory.fill(base + start, value, (end - start)); } else { for (; start < end; ++start) { - STORE>(buffer, start, value, byteOffset); + store(base + (start << alignof()), value); } } return array; } -@inline -export function SORT, T>( +@inline export function SORT, T>( array: TArray, comparator: (a: T, b: T) => i32 ): TArray { - var byteOffset = array.byteOffset; var length = array.length; + var offset = array.byteOffset; if (length <= 1) return array; - var buffer = array.buffer; + var buffer = changetype(array.buffer); if (length == 2) { - let a = LOAD(buffer, 1, byteOffset); - let b = LOAD(buffer, 0, byteOffset); + let a = load(buffer + offset, sizeof()); + let b = load(buffer + offset); if (comparator(a, b) < 0) { - STORE(buffer, 1, b, byteOffset); - STORE(buffer, 0, a, byteOffset); + store(buffer + offset, b, sizeof()); + store(buffer + offset, a); } return array; } - SORT_IMPL(buffer, byteOffset, length, comparator); + // TODO + // SORT_IMPL(buffer, byteOffset, length, comparator); return array; } -@inline -export function SUBARRAY, T>( +@inline export function SUBARRAY, T>( array: TArray, begin: i32, end: i32 ): TArray { + var buffer = changetype(array.buffer); var length = array.length; if (begin < 0) begin = max(length + begin, 0); else begin = min(begin, length); if (end < 0) end = max(length + end, begin); else end = max(min(end, length), begin); - var slice = memory.allocate(offsetof()); - store(slice, array.buffer, offsetof("buffer")); - store(slice, array.byteOffset + (begin << alignof()), offsetof("byteOffset")); - store(slice, (end - begin) << alignof(), offsetof("byteLength")); - return changetype(slice); + var out = ALLOC(offsetof()); + store(out, buffer, offsetof("buffer")); + store(out, array.byteOffset + (begin << alignof()), offsetof("byteOffset")); + store(out, (end - begin) << alignof(), offsetof("byteLength")); + LINK(buffer, REGISTER(out)); // register first, then link + return changetype(out); } -@inline -export function REDUCE, T, TRet>( +@inline export function REDUCE, T, TRet>( array: TArray, callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet, initialValue: TRet ): TRet { - var length = array.length; - var buffer = array.buffer; - var byteOffset = array.byteOffset; - for (let i = 0; i < length; i++) { - initialValue = callbackfn( - initialValue, - LOAD(buffer, i, byteOffset), - i, - array, - ); + var base = changetype(array.buffer) + array.byteOffset; + for (let i = 0, k = array.length; i < k; i++) { + initialValue = callbackfn(initialValue, load(base + (i << alignof())), i, array); } return initialValue; } -@inline -export function REDUCE_RIGHT, T, TRet>( +@inline export function REDUCE_RIGHT, T, TRet>( array: TArray, callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet, initialValue: TRet ): TRet { - var buffer = array.buffer; - var byteOffset = array.byteOffset; + var base = changetype(array.buffer) + array.byteOffset; for (let i = array.length - 1; i >= 0; i--) { - initialValue = callbackfn( - initialValue, - LOAD(buffer, i, byteOffset), - i, - array, - ); + initialValue = callbackfn(initialValue, load(base + (i << alignof())), i, array); } return initialValue; } -@inline -export function MAP, T>( +@inline export function MAP, T>( array: TArray, callbackfn: (value: T, index: i32, self: TArray) => T, ): TArray { var length = array.length; - var buffer = array.buffer; - var byteOffset = array.byteOffset; + var base = changetype(array.buffer) + array.byteOffset; var result = instantiate(length); - var resultBuffer = result.buffer; + var resultBase = changetype(result.buffer); // assumes byteOffset = 0 for (let i = 0; i < length; i++) { - STORE>(resultBuffer, i, >callbackfn(LOAD(buffer, i, byteOffset), i, array)); + store( + resultBase + (i << alignof()), + callbackfn(load(base + (i << alignof())), i, array) + ); } return result; } -@inline -export function FIND_INDEX, T>( +@inline export function FIND_INDEX, T>( array: TArray, callbackfn: (value: T, index: i32, array: TArray) => bool, ): i32 { - var length = array.length; - var buffer = array.buffer; - var byteOffset = array.byteOffset; - for (let i = 0; i < length; i++) { - if (callbackfn(LOAD(buffer, i, byteOffset), i, array)) { - return i; - } + var base = changetype(array.buffer) + array.byteOffset; + for (let i = 0, k = array.length; i < k; i++) { + if (callbackfn(load(base + (i << alignof())), i, array)) return i; } return -1; } -@inline -export function SOME, T>( +@inline export function SOME, T>( array: TArray, callbackfn: (value: T, index: i32, array: TArray) => bool, ): bool { - var length = array.length; - var buffer = array.buffer; - var byteOffset = array.byteOffset; - for (let i = 0; i < length; i++) { - if (callbackfn(LOAD(buffer, i, byteOffset), i, array)) { - return true; - } + var base = changetype(array.buffer) + array.byteOffset; + for (let i = 0, k = array.length; i < k; i++) { + if (callbackfn(load(base + (i << alignof())), i, array)) return true; } return false; } -@inline -export function EVERY, T>( +@inline export function EVERY, T>( array: TArray, callbackfn: (value: T, index: i32, array: TArray) => bool, ): bool { - var length = array.length; - var buffer = array.buffer; - var byteOffset = array.byteOffset; - for (let i = 0; i < length; i++) { - if (callbackfn(LOAD(buffer, i, byteOffset), i, array)) { - continue; - } + var base = changetype(array.buffer) + array.byteOffset; + for (let i = 0, k = array.length; i < k; i++) { + if (callbackfn(load(base + (i << alignof())), i, array)) continue; return false; } return true; } -@inline -export function FOREACH, T>( +@inline export function FOREACH, T>( array: TArray, callbackfn: (value: T, index: i32, array: TArray) => void, ): void { - var length = array.length; - var buffer = array.buffer; - var byteOffset = array.byteOffset; - for (let i = 0; i < length; i++) { - callbackfn(LOAD(buffer, i, byteOffset), i, array); + var base = changetype(array.buffer) + array.byteOffset; + for (let i = 0, k = array.length; i < k; i++) { + callbackfn(load(base + (i << alignof())), i, array); } } diff --git a/std/assembly/runtime.ts b/std/assembly/runtime.ts index 157ce440..d830bf59 100644 --- a/std/assembly/runtime.ts +++ b/std/assembly/runtime.ts @@ -43,8 +43,8 @@ export function ADJUST(payloadSize: usize): usize { return 1 << (32 - clz(payloadSize + HEADER_SIZE - 1)); } -/** Allocates a new object and returns a pointer to its payload. */ -@unsafe export function ALLOC(payloadSize: u32): usize { +/** Allocates a new object and returns a pointer to its payload. Does not fill. */ +@unsafe export function ALLOC_RAW(payloadSize: u32): usize { var header = changetype
(memory.allocate(ADJUST(payloadSize))); header.classId = HEADER_MAGIC; header.payloadSize = payloadSize; @@ -52,7 +52,12 @@ export function ADJUST(payloadSize: usize): usize { header.gc1 = 0; header.gc2 = 0; } - var ref = changetype(header) + HEADER_SIZE; + return changetype(header) + HEADER_SIZE; +} + +/** Allocates a new object and returns a pointer to its payload. Fills with zeroes.*/ +@unsafe export function ALLOC(payloadSize: u32): usize { + var ref = ALLOC_RAW(payloadSize); memory.fill(ref, 0, payloadSize); return ref; } @@ -110,12 +115,12 @@ function unref(ref: usize): HEADER { } /** Registers a managed object. Cannot be free'd anymore afterwards. */ -@unsafe @inline export function REGISTER(ref: usize): T { +@unsafe @inline export function REGISTER(ref: usize): TRet { // see comment in REALLOC why this is useful. also inline this because // it's generic so we don't get a bunch of functions. unref(ref).classId = gc.classId(); if (GC) gc.register(ref); - return changetype(ref); + return changetype(ref); } /** Links a managed object with its managed parent. */ @@ -132,6 +137,41 @@ export abstract class ArrayBufferBase { get byteLength(): i32 { return changetype
(changetype(this) - HEADER_SIZE).payloadSize; } + constructor(length: i32) { + if (length > ArrayBufferBase.MAX_BYTELENGTH) throw new RangeError("Invalid array buffer length"); + return REGISTER(ALLOC(length)); + } +} + +/** Typed array base class. */ +export abstract class ArrayBufferView { + [key: number]: number; + readonly buffer: ArrayBuffer; + @unsafe dataStart: usize; + @unsafe dataEnd: usize; + + constructor(length: i32, alignLog2: i32) { + if (length > ArrayBufferBase.MAX_BYTELENGTH >> alignLog2) { + throw new RangeError("Invalid typed array length"); + } + var byteLength = length << alignLog2; + var buffer = new ArrayBuffer(byteLength); + this.buffer = buffer; + this.dataStart = changetype(buffer); + this.dataEnd = changetype(buffer) + length; + } + + get byteOffset(): i32 { + return this.dataStart - changetype(this.buffer); + } + + get byteLength(): i32 { + return this.dataEnd - this.dataStart; + } + + get length(): i32 { + return unreachable(); + } } /** String base class. */ diff --git a/std/assembly/typedarray.ts b/std/assembly/typedarray.ts index a894b66b..bc3e0c23 100644 --- a/std/assembly/typedarray.ts +++ b/std/assembly/typedarray.ts @@ -1,16 +1,9 @@ import { - TypedArray, - FILL, - SORT, - SUBARRAY, - REDUCE, - REDUCE_RIGHT, - MAP, - FIND_INDEX, - SOME, - EVERY, - FOREACH, -} from "./internal/typedarray"; + ALLOC, + REGISTER, + LINK, + ArrayBufferView +} from "./runtime"; import { COMPARATOR @@ -20,9 +13,15 @@ function clampToByte(value: i32): i32 { return ~(value >> 31) & (((255 - value) >> 31) | value); // & 255 } -export class Int8Array extends TypedArray { +export class Int8Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 0); + } + + get length(): i32 { return this.byteLength; } + fill(value: i32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int8Array { return FILL(this, value, start, end); } @@ -70,9 +69,15 @@ export class Int8Array extends TypedArray { } } -export class Uint8Array extends TypedArray { +export class Uint8Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 0); + } + + get length(): i32 { return this.byteLength; } + fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint8Array { return FILL(this, value, start, end); } @@ -123,16 +128,6 @@ export class Uint8Array extends TypedArray { export class Uint8ClampedArray extends Uint8Array { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); - @inline @operator("[]=") - protected __set(index: i32, value: i32): void { - super.__set(index, clampToByte(value)); - } - - @inline @operator("{}=") - protected __unchecked_set(index: i32, value: i32): void { - super.__unchecked_set(index, clampToByte(value)); - } - fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint8ClampedArray { return changetype(super.fill(value, start, end)); // safe because '.fill' reuses 'this' } @@ -180,9 +175,15 @@ export class Uint8ClampedArray extends Uint8Array { } } -export class Int16Array extends TypedArray { +export class Int16Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 1); + } + + get length(): i32 { return this.byteLength >>> 1; } + fill(value: i32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int16Array { return FILL(this, value, start, end); } @@ -230,9 +231,15 @@ export class Int16Array extends TypedArray { } } -export class Uint16Array extends TypedArray { +export class Uint16Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 1); + } + + get length(): i32 { return this.byteLength >>> 1; } + fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint16Array { return FILL(this, value, start, end); } @@ -280,9 +287,15 @@ export class Uint16Array extends TypedArray { } } -export class Int32Array extends TypedArray { +export class Int32Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 2); + } + + get length(): i32 { return this.byteLength >>> 2; } + fill(value: i32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int32Array { return FILL(this, value, start, end); } @@ -330,9 +343,15 @@ export class Int32Array extends TypedArray { } } -export class Uint32Array extends TypedArray { +export class Uint32Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 2); + } + + get length(): i32 { return this.byteLength >>> 2; } + fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint32Array { return FILL(this, value, start, end); } @@ -380,9 +399,15 @@ export class Uint32Array extends TypedArray { } } -export class Int64Array extends TypedArray { +export class Int64Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 3); + } + + get length(): i32 { return this.byteLength >>> 3; } + fill(value: i64, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int64Array { return FILL(this, value, start, end); } @@ -430,9 +455,15 @@ export class Int64Array extends TypedArray { } } -export class Uint64Array extends TypedArray { +export class Uint64Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 3); + } + + get length(): i32 { return this.byteLength >>> 3; } + fill(value: u64, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint64Array { return FILL(this, value, start, end); } @@ -480,9 +511,15 @@ export class Uint64Array extends TypedArray { } } -export class Float32Array extends TypedArray { +export class Float32Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 2); + } + + get length(): i32 { return this.byteLength >>> 2; } + fill(value: f32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Float32Array { return FILL(this, value, start, end); } @@ -530,9 +567,15 @@ export class Float32Array extends TypedArray { } } -export class Float64Array extends TypedArray { +export class Float64Array extends ArrayBufferView { @lazy static readonly BYTES_PER_ELEMENT: usize = sizeof(); + constructor(length: i32) { + super(length, 3); + } + + get length(): i32 { return this.byteLength >>> 3; } + fill(value: f64, start: i32 = 0, end: i32 = i32.MAX_VALUE): Float64Array { return FILL(this, value, start, end); } @@ -579,3 +622,148 @@ export class Float64Array extends TypedArray { FOREACH(this, callbackfn); } } + +@inline function FILL( + array: TArray, + value: native, + start: i32, + end: i32 +): TArray { + var dataStart = array.dataStart; + var length = array.length; + start = start < 0 ? max(length + start, 0) : min(start, length); + end = end < 0 ? max(length + end, 0) : min(end, length); + if (sizeof() == 1) { + if (start < end) memory.fill(dataStart + start, value, (end - start)); + } else { + for (; start < end; ++start) { + store(dataStart + (start << alignof()), value); + } + } + return array; +} + +@inline function SORT( + array: TArray, + comparator: (a: T, b: T) => i32 +): TArray { + var length = array.length; + if (length <= 1) return array; + var base = array.dataStart; + if (length == 2) { + let a = load(base, sizeof()); + let b = load(base); + if (comparator(a, b) < 0) { + store(base, b, sizeof()); + store(base, a); + } + return array; + } + // TODO + // SORT_IMPL(buffer, byteOffset, length, comparator); + return array; +} + +@inline function SUBARRAY( + array: TArray, + begin: i32, + end: i32 +): TArray { + var buffer = changetype(array.buffer); + var length = array.length; + if (begin < 0) begin = max(length + begin, 0); + else begin = min(begin, length); + if (end < 0) end = max(length + end, begin); + else end = max(min(end, length), begin); + var out = ALLOC(offsetof()); + store(out, buffer, offsetof("buffer")); + store(out, array.dataStart + (begin << alignof()) , offsetof("dataStart")); + store(out, array.dataEnd + ((end - begin) << alignof()), offsetof("dataEnd")); + LINK(buffer, REGISTER(out)); // register first, then link + return changetype(out); +} + +@inline function REDUCE( + array: TArray, + callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet, + initialValue: TRet +): TRet { + var dataStart = array.dataStart; + for (let i = 0, k = array.length; i < k; i++) { + initialValue = callbackfn(initialValue, load(dataStart + (i << alignof())), i, array); + } + return initialValue; +} + +@inline function REDUCE_RIGHT( + array: TArray, + callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet, + initialValue: TRet +): TRet { + var dataStart = array.dataStart; + for (let i = array.length - 1; i >= 0; i--) { + initialValue = callbackfn(initialValue, load(dataStart + (i << alignof())), i, array); + } + return initialValue; +} + +@inline function MAP( + array: TArray, + callbackfn: (value: T, index: i32, self: TArray) => T, +): TArray { + var length = array.length; + var dataStart = array.dataStart; + var out = instantiate(length); + var outDataStart = out.dataStart; + for (let i = 0; i < length; i++) { + store( + outDataStart + (i << alignof()), + callbackfn(load(dataStart + (i << alignof())), i, array) + ); + } + return out; +} + +@inline function FIND_INDEX( + array: TArray, + callbackfn: (value: T, index: i32, array: TArray) => bool, +): i32 { + var dataStart = array.dataStart; + for (let i = 0, k = array.length; i < k; i++) { + if (callbackfn(load(dataStart + (i << alignof())), i, array)) return i; + } + return -1; +} + +@inline function SOME( + array: TArray, + callbackfn: (value: T, index: i32, array: TArray) => bool, +): bool { + var dataStart = array.dataStart; + for (let i = 0, k = array.length; i < k; i++) { + if (callbackfn(load(dataStart + (i << alignof())), i, array)) return true; + } + return false; +} + +@inline function EVERY( + array: TArray, + callbackfn: (value: T, index: i32, array: TArray) => bool, +): bool { + var dataStart = array.dataStart; + for (let i = 0, k = array.length; i < k; i++) { + if (callbackfn(load(dataStart + (i << alignof())), i, array)) continue; + return false; + } + return true; +} + +@inline function FOREACH( + array: TArray, + callbackfn: (value: T, index: i32, array: TArray) => void, +): void { + var dataStart = array.dataStart; + for (let i = 0, k = array.length; i < k; i++) { + callbackfn(load(dataStart + (i << alignof())), i, array); + } +} diff --git a/tests/compiler/std/runtime.optimized.wat b/tests/compiler/std/runtime.optimized.wat index 2dcfc063..edf17573 100644 --- a/tests/compiler/std/runtime.optimized.wat +++ b/tests/compiler/std/runtime.optimized.wat @@ -1173,7 +1173,34 @@ local.get $0 call $~lib/allocator/tlsf/Root#use ) - (func $~lib/internal/memory/memset (; 17 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $~lib/runtime/ALLOC_RAW (; 17 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + i32.const 1 + i32.const 32 + local.get $0 + i32.const 15 + i32.add + i32.clz + i32.sub + i32.shl + call $~lib/allocator/tlsf/memory.allocate + local.tee $1 + i32.const -1520547049 + i32.store + local.get $1 + local.get $0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=12 + local.get $1 + i32.const 16 + i32.add + ) + (func $~lib/internal/memory/memset (; 18 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) (local $2 i32) local.get $1 i32.eqz @@ -1392,38 +1419,16 @@ end end ) - (func $~lib/runtime/ALLOC (; 18 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/runtime/ALLOC (; 19 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) - i32.const 1 - i32.const 32 local.get $0 - i32.const 15 - i32.add - i32.clz - i32.sub - i32.shl - call $~lib/allocator/tlsf/memory.allocate - local.tee $1 - i32.const -1520547049 - i32.store - local.get $1 - local.get $0 - i32.store offset=4 - local.get $1 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 0 - i32.store offset=12 - local.get $1 - i32.const 16 - i32.add + call $~lib/runtime/ALLOC_RAW local.tee $1 local.get $0 call $~lib/internal/memory/memset local.get $1 ) - (func $~lib/internal/memory/memcpy (; 19 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/internal/memory/memcpy (; 20 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -2320,7 +2325,7 @@ i32.store8 end ) - (func $~lib/internal/memory/memmove (; 20 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/internal/memory/memmove (; 21 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) local.get $0 @@ -2518,7 +2523,7 @@ end end ) - (func $~lib/allocator/tlsf/memory.free (; 21 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/allocator/tlsf/memory.free (; 22 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) @@ -2556,7 +2561,7 @@ end end ) - (func $~lib/runtime/REALLOC (; 22 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/runtime/REALLOC (; 23 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -2630,7 +2635,7 @@ if i32.const 0 i32.const 184 - i32.const 83 + i32.const 88 i32.const 8 call $~lib/env/abort unreachable @@ -2657,14 +2662,14 @@ i32.store offset=4 local.get $0 ) - (func $~lib/runtime/unref (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/runtime/unref (; 24 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 i32.const 232 i32.lt_u if i32.const 0 i32.const 184 - i32.const 101 + i32.const 106 i32.const 2 call $~lib/env/abort unreachable @@ -2679,18 +2684,18 @@ if i32.const 0 i32.const 184 - i32.const 103 + i32.const 108 i32.const 2 call $~lib/env/abort unreachable end local.get $0 ) - (func $std/runtime/gc.register (; 24 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $std/runtime/gc.register (; 25 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 global.set $std/runtime/register_ref ) - (func $start:std/runtime (; 25 ;) (type $FUNCSIG$v) + (func $start:std/runtime (; 26 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) @@ -3015,13 +3020,13 @@ unreachable end ) - (func $std/runtime/gc.link (; 26 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $std/runtime/gc.link (; 27 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) nop ) - (func $~lib/runtime/gc.collect (; 27 ;) (type $FUNCSIG$v) + (func $~lib/runtime/gc.collect (; 28 ;) (type $FUNCSIG$v) nop ) - (func $start (; 28 ;) (type $FUNCSIG$v) + (func $start (; 29 ;) (type $FUNCSIG$v) call $start:std/runtime ) ) diff --git a/tests/compiler/std/runtime.untouched.wat b/tests/compiler/std/runtime.untouched.wat index 69d6344d..76644b5c 100644 --- a/tests/compiler/std/runtime.untouched.wat +++ b/tests/compiler/std/runtime.untouched.wat @@ -1461,7 +1461,29 @@ local.get $0 call $~lib/allocator/tlsf/Root#use ) - (func $~lib/internal/memory/memset (; 23 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/runtime/ALLOC_RAW (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + local.get $0 + call $~lib/runtime/ADJUST + call $~lib/allocator/tlsf/memory.allocate + local.set $1 + local.get $1 + i32.const -1520547049 + i32.store + local.get $1 + local.get $0 + i32.store offset=4 + local.get $1 + i32.const 0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=12 + local.get $1 + i32.const 16 + i32.add + ) + (func $~lib/internal/memory/memset (; 24 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) (local $5 i64) @@ -1715,47 +1737,29 @@ end end ) - (func $~lib/runtime/ALLOC (; 24 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/runtime/ALLOC (; 25 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) - (local $5 i32) local.get $0 - call $~lib/runtime/ADJUST - call $~lib/allocator/tlsf/memory.allocate + call $~lib/runtime/ALLOC_RAW local.set $1 - local.get $1 - i32.const -1520547049 - i32.store - local.get $1 - local.get $0 - i32.store offset=4 - local.get $1 - i32.const 0 - i32.store offset=8 - local.get $1 - i32.const 0 - i32.store offset=12 - local.get $1 - i32.const 16 - i32.add - local.set $2 block $~lib/runtime/memory.fill|inlined.0 - local.get $2 - local.set $3 + local.get $1 + local.set $2 i32.const 0 - local.set $4 + local.set $3 local.get $0 - local.set $5 + local.set $4 + local.get $2 local.get $3 local.get $4 - local.get $5 call $~lib/internal/memory/memset end - local.get $2 + local.get $1 ) - (func $~lib/internal/memory/memcpy (; 25 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/internal/memory/memcpy (; 26 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) @@ -2956,7 +2960,7 @@ i32.store8 end ) - (func $~lib/internal/memory/memmove (; 26 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) + (func $~lib/internal/memory/memmove (; 27 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32) (local $3 i32) local.get $0 local.get $1 @@ -3183,7 +3187,7 @@ end end ) - (func $~lib/allocator/tlsf/memory.free (; 27 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/allocator/tlsf/memory.free (; 28 ;) (type $FUNCSIG$vi) (param $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) @@ -3226,7 +3230,7 @@ end end ) - (func $~lib/runtime/REALLOC (; 28 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (func $~lib/runtime/REALLOC (; 29 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) (local $2 i32) (local $3 i32) (local $4 i32) @@ -3315,7 +3319,7 @@ if i32.const 0 i32.const 184 - i32.const 83 + i32.const 88 i32.const 8 call $~lib/env/abort unreachable @@ -3351,7 +3355,7 @@ i32.store offset=4 local.get $0 ) - (func $~lib/runtime/unref (; 29 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/runtime/unref (; 30 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) (local $1 i32) local.get $0 global.get $~lib/runtime/HEAP_BASE @@ -3362,7 +3366,7 @@ if i32.const 0 i32.const 184 - i32.const 101 + i32.const 106 i32.const 2 call $~lib/env/abort unreachable @@ -3379,29 +3383,29 @@ if i32.const 0 i32.const 184 - i32.const 103 + i32.const 108 i32.const 2 call $~lib/env/abort unreachable end local.get $1 ) - (func $~lib/runtime/FREE (; 30 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $~lib/runtime/FREE (; 31 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 call $~lib/runtime/unref call $~lib/allocator/tlsf/memory.free ) - (func $std/runtime/gc.register (; 31 ;) (type $FUNCSIG$vi) (param $0 i32) + (func $std/runtime/gc.register (; 32 ;) (type $FUNCSIG$vi) (param $0 i32) local.get $0 global.set $std/runtime/register_ref ) - (func $~lib/runtime/ArrayBufferBase#get:byteLength (; 32 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/runtime/ArrayBufferBase#get:byteLength (; 33 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 i32.const 16 i32.sub i32.load offset=4 ) - (func $~lib/runtime/StringBase#get:length (; 33 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (func $~lib/runtime/StringBase#get:length (; 34 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) local.get $0 i32.const 16 i32.sub @@ -3409,7 +3413,7 @@ i32.const 1 i32.shr_u ) - (func $start:std/runtime (; 34 ;) (type $FUNCSIG$v) + (func $start:std/runtime (; 35 ;) (type $FUNCSIG$v) (local $0 i32) call $start:~lib/allocator/tlsf i32.const 2 @@ -3654,7 +3658,7 @@ global.get $std/runtime/barrier1 call $~lib/runtime/ALLOC global.set $std/runtime/ref4 - block $~lib/runtime/REGISTER|inlined.0 (result i32) + block $~lib/runtime/REGISTER|inlined.0 (result i32) global.get $std/runtime/ref4 local.set $0 local.get $0 @@ -3738,15 +3742,15 @@ unreachable end ) - (func $std/runtime/gc.link (; 35 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) + (func $std/runtime/gc.link (; 36 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32) nop ) - (func $~lib/runtime/gc.collect (; 36 ;) (type $FUNCSIG$v) + (func $~lib/runtime/gc.collect (; 37 ;) (type $FUNCSIG$v) nop ) - (func $start (; 37 ;) (type $FUNCSIG$v) + (func $start (; 38 ;) (type $FUNCSIG$v) call $start:std/runtime ) - (func $null (; 38 ;) (type $FUNCSIG$v) + (func $null (; 39 ;) (type $FUNCSIG$v) ) )