diff --git a/assembly.d.ts b/assembly.d.ts index 3adb3865..a31a6652 100644 --- a/assembly.d.ts +++ b/assembly.d.ts @@ -115,4 +115,10 @@ interface IArguments {} interface Number {} interface Object {} interface RegExp {} -interface String {} + +declare class String { + static fromCharCode(ls: i32, hs?: i32): string; + static fromCharCodes(arr: u16[]): string; + static fromCodePoint(cp: i32): string; + static fromCodePoints(arr: i32[]): string; +} diff --git a/portable.d.ts b/portable.d.ts index 95ce426c..05319dca 100644 --- a/portable.d.ts +++ b/portable.d.ts @@ -83,6 +83,9 @@ declare class Int32Array extends Array {} declare class String { static fromCharCode(ls: i32, hs?: i32): string; + static fromCharCodes(arr: u16[]): string; + static fromCodePoint(cp: i32): string; + static fromCodePoints(arr: i32[]): string; readonly length: i32; indexOf(subject: string): i32; charCodeAt(index: i32): i32; diff --git a/portable.js b/portable.js index 7d34b6d7..f8d23c01 100644 --- a/portable.js +++ b/portable.js @@ -28,3 +28,6 @@ AssertionError.prototype.message = "assertion failed"; globalScope["assert"] = function assert(isTrue) { if (!isTrue) throw new AssertionError(); }; globalScope["changetype"] = function changetype(value) { return value; } + +String["fromCharCodes"] = function fromCharCodes(arr) { return String.fromCharCode.apply(String, arr); } +String["fromCodePoints"] = function fromCodePoints(arr) { return String.fromCodePoint.apply(String, arr); } diff --git a/src/builtins.ts b/src/builtins.ts index ec94a3c2..99f7c6dd 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -156,7 +156,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty : module.createUnary(UnaryOp.AbsF64, arg0); if (typeArguments[0].isAnyInteger) { if (typeArguments[0].isSignedInteger) { - tempLocal0 = compiler.currentFunction.addLocal(typeArguments[0]); + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(typeArguments[0]); if (typeArguments[0].isLongInteger) return module.createSelect( module.createBinary(BinaryOp.SubI64, @@ -198,8 +198,9 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty ? module.createBinary(BinaryOp.MaxF32, arg0, arg1) : module.createBinary(BinaryOp.MaxF64, arg0, arg1); if (typeArguments[0].isAnyInteger) { - tempLocal0 = compiler.currentFunction.addLocal(typeArguments[0]); - tempLocal1 = compiler.currentFunction.addLocal(typeArguments[0]); + tempLocal0 = compiler.currentFunction.getTempLocal(typeArguments[0]); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(typeArguments[0]); + compiler.currentFunction.freeTempLocal(tempLocal0); if (typeArguments[0].isLongInteger) return module.createSelect( module.createTeeLocal(tempLocal0.index, arg0), @@ -233,8 +234,9 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty ? module.createBinary(BinaryOp.MinF32, arg0, arg1) : module.createBinary(BinaryOp.MinF64, arg0, arg1); if (typeArguments[0].isAnyInteger) { - tempLocal0 = compiler.currentFunction.addLocal(typeArguments[0]); - tempLocal1 = compiler.currentFunction.addLocal(typeArguments[0]); + tempLocal0 = compiler.currentFunction.getTempLocal(typeArguments[0]); + tempLocal1 = compiler.currentFunction.getAndFreeTempLocal(typeArguments[0]); + compiler.currentFunction.freeTempLocal(tempLocal0); if (typeArguments[0].isLongInteger) return module.createSelect( module.createTeeLocal(tempLocal0.index, arg0), @@ -428,13 +430,13 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports compiler.currentType = Type.bool; if (typeArguments[0] == Type.f32) { - tempLocal0 = compiler.currentFunction.addLocal(Type.f32); + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f32); return module.createBinary(BinaryOp.NeF32, module.createTeeLocal(tempLocal0.index, arg0), module.createGetLocal(tempLocal0.index, NativeType.F32) ); } else { - tempLocal0 = compiler.currentFunction.addLocal(Type.f64); + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f64); return module.createBinary(BinaryOp.NeF64, module.createTeeLocal(tempLocal0.index, arg0), module.createGetLocal(tempLocal0.index, NativeType.F64) @@ -453,7 +455,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty arg0 = compiler.compileExpression(operands[0], typeArguments[0]); // reports compiler.currentType = Type.bool; if (typeArguments[0] == Type.f32) { - tempLocal0 = compiler.currentFunction.addLocal(Type.f32); + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f32); return module.createSelect( module.createBinary(BinaryOp.NeF32, module.createUnary(UnaryOp.AbsF32, @@ -468,7 +470,7 @@ export function compileCall(compiler: Compiler, prototype: FunctionPrototype, ty ) ); } else { - tempLocal0 = compiler.currentFunction.addLocal(Type.f64); + tempLocal0 = compiler.currentFunction.getAndFreeTempLocal(Type.f64); return module.createSelect( module.createBinary(BinaryOp.NeF64, module.createUnary(UnaryOp.AbsF64, diff --git a/src/compiler.ts b/src/compiler.ts index 865e0206..87906c1f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -11,28 +11,7 @@ import { NativeType, FunctionTypeRef, FunctionRef, - ExpressionId, - - getExpressionId, - getExpressionType, - getFunctionBody, - getConstValueI32, - getConstValueI64Low, - getConstValueI64High, - getConstValueF32, - getConstValueF64, - getGetLocalIndex, - getGetGlobalName, - isLoadAtomic, - isLoadSigned, - getLoadBytes, - getLoadOffset, - getLoadPtr, - getUnaryOp, - getUnaryValue, - getBinaryOp, - getBinaryLeft, - getBinaryRight + ExpressionId } from "./module"; import { @@ -373,10 +352,10 @@ export class Compiler extends DiagnosticEmitter { } else if (declaration) { if (declaration.initializer) { initializer = this.compileExpression(declaration.initializer, type); - if (getExpressionId(initializer) != ExpressionId.Const) { + if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { if (!element.isMutable) { initializer = this.precomputeExpressionRef(initializer); - if (getExpressionId(initializer) != ExpressionId.Const) { + if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range); initializeInStart = true; } @@ -395,19 +374,19 @@ export class Compiler extends DiagnosticEmitter { this.module.addGlobal(internalName, nativeType, element.isMutable, initializer); if (!element.isMutable) { element.hasConstantValue = true; - const exprType: NativeType = getExpressionType(initializer); + const exprType: NativeType = _BinaryenExpressionGetType(initializer); switch (exprType) { case NativeType.I32: - element.constantIntegerValue = new I64(getConstValueI32(initializer), 0); + element.constantIntegerValue = new I64(_BinaryenConstGetValueI32(initializer), 0); break; case NativeType.I64: - element.constantIntegerValue = new I64(getConstValueI64Low(initializer), getConstValueI64High(initializer)); + element.constantIntegerValue = new I64(_BinaryenConstGetValueI64Low(initializer), _BinaryenConstGetValueI64High(initializer)); break; case NativeType.F32: - element.constantFloatValue = getConstValueF32(initializer); + element.constantFloatValue = _BinaryenConstGetValueF32(initializer); break; case NativeType.F64: - element.constantFloatValue = getConstValueF64(initializer); + element.constantFloatValue = _BinaryenConstGetValueF64(initializer); break; default: throw new Error("unexpected initializer type"); @@ -439,9 +418,9 @@ export class Compiler extends DiagnosticEmitter { let initializeInStart: bool = false; if (declaration.value) { initializer = this.compileExpression(declaration.value, Type.i32); - if (getExpressionId(initializer) != ExpressionId.Const) { + if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { initializer = this.precomputeExpressionRef(initializer); - if (getExpressionId(initializer) != ExpressionId.Const) { + if (_BinaryenExpressionGetId(initializer) != ExpressionId.Const) { if (element.isConstant) this.warning(DiagnosticCode.Compiling_constant_global_with_non_constant_initializer_as_mutable, declaration.range); initializeInStart = true; @@ -466,9 +445,9 @@ export class Compiler extends DiagnosticEmitter { this.startFunctionBody.push(this.module.createSetGlobal(val.internalName, initializer)); } else { this.module.addGlobal(val.internalName, NativeType.I32, false, initializer); - if (getExpressionType(initializer) == NativeType.I32) { + if (_BinaryenExpressionGetType(initializer) == NativeType.I32) { val.hasConstantValue = true; - val.constantValue = getConstValueI32(initializer); + val.constantValue = _BinaryenConstGetValueI32(initializer); } else throw new Error("unexpected initializer type"); } @@ -1068,7 +1047,7 @@ export class Compiler extends DiagnosticEmitter { typeRef = this.module.addFunctionType(typeToSignatureNamePart(this.currentType), nativeType, []); const funcRef: FunctionRef = this.module.addFunction("__precompute", typeRef, [], expr); this.module.runPasses([ "precompute" ], funcRef); - const ret: ExpressionRef = getFunctionBody(funcRef); + const ret: ExpressionRef = _BinaryenFunctionGetBody(funcRef); this.module.removeFunction("__precompute"); // TODO: also remove the function type somehow if no longer used or make the C-API accept // a `null` typeRef, using an implicit type. @@ -1240,41 +1219,6 @@ export class Compiler extends DiagnosticEmitter { return expr; } - cloneExpressionRef(expr: ExpressionRef, noSideEffects: bool = false, maxDepth: i32 = 0x7fffffff): ExpressionRef { - // currently supports side effect free expressions only - if (maxDepth < 0) - return 0; - let nested1: ExpressionRef, - nested2: ExpressionRef; - switch (getExpressionId(expr)) { - case ExpressionId.Const: - switch (getExpressionType(expr)) { - case NativeType.I32: return this.module.createI32(getConstValueI32(expr)); - case NativeType.I64: return this.module.createI64(getConstValueI64Low(expr), getConstValueI64High(expr)); - case NativeType.F32: return this.module.createF32(getConstValueF32(expr)); - case NativeType.F64: return this.module.createF64(getConstValueF64(expr)); - default: throw new Error("unexpected expression type"); - } - case ExpressionId.GetLocal: - return this.module.createGetLocal(getGetLocalIndex(expr), getExpressionType(expr)); - // case ExpressionId.GetGlobal: explodes if it doesn't have a name - // return this.module.createGetGlobal(getGetGlobalName(expr), getExpressionType(expr)); - case ExpressionId.Load: - if (!(nested1 = this.cloneExpressionRef(getLoadPtr(expr), noSideEffects, maxDepth - 1))) break; - return isLoadAtomic(expr) - ? this.module.createAtomicLoad(getLoadBytes(expr), nested1, getExpressionType(expr), getLoadOffset(expr)) - : this.module.createLoad(getLoadBytes(expr), isLoadSigned(expr), nested1, getExpressionType(expr), getLoadOffset(expr)); - case ExpressionId.Unary: - if (!(nested1 = this.cloneExpressionRef(getUnaryValue(expr), noSideEffects, maxDepth - 1))) break; - return this.module.createUnary(getUnaryOp(expr), nested1); - case ExpressionId.Binary: - if (!(nested1 = this.cloneExpressionRef(getBinaryLeft(expr), noSideEffects, maxDepth - 1))) break; - if (!(nested2 = this.cloneExpressionRef(getBinaryLeft(expr), noSideEffects, maxDepth - 1))) break; - return this.module.createBinary(getBinaryOp(expr), nested1, nested2); - } - return 0; - } - compileAssertionExpression(expression: AssertionExpression, contextualType: Type): ExpressionRef { const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments); // reports if (!toType) @@ -1537,7 +1481,7 @@ export class Compiler extends DiagnosticEmitter { right = this.compileExpression(expression.right, this.currentType); // simplify if left is free of side effects while tolerating two levels of nesting, e.g., i32.load(i32.load(i32.const)) - // if (condition = this.cloneExpressionRef(left, true, 2)) + // if (condition = this.module.cloneExpression(left, true, 2)) // return this.module.createIf( // this.currentType.isLongInteger // ? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) @@ -1570,7 +1514,7 @@ export class Compiler extends DiagnosticEmitter { right = this.compileExpression(expression.right, this.currentType); // simplify if left is free of side effects while tolerating two levels of nesting - // if (condition = this.cloneExpressionRef(left, true, 2)) + // if (condition = this.module.cloneExpression(left, true, 2)) // return this.module.createIf( // this.currentType.isLongInteger // ? this.module.createBinary(BinaryOp.NeI64, condition, this.module.createI64(0, 0)) diff --git a/src/decompiler.ts b/src/decompiler.ts index 719ff9cb..07f0d34a 100644 --- a/src/decompiler.ts +++ b/src/decompiler.ts @@ -1,4 +1,5 @@ import { + Module, NativeType, ExpressionId, @@ -9,7 +10,9 @@ import { FunctionRef, ExpressionRef, Index, + readString + } from "./module"; import { I64 } from "./util"; diff --git a/src/module.ts b/src/module.ts index 2402e4ff..d28108da 100644 --- a/src/module.ts +++ b/src/module.ts @@ -751,7 +751,7 @@ export class Module { } interpret(): void { - return _BinaryenModuleInterpret(this.ref); + _BinaryenModuleInterpret(this.ref); } write(output: usize, outputSize: usize = 1048576): usize { @@ -759,11 +759,11 @@ export class Module { } print(): void { - return _BinaryenModulePrint(this.ref); + _BinaryenModulePrint(this.ref); } printAsmjs(): void { - return _BinaryenModulePrintAsmjs(this.ref); + _BinaryenModulePrintAsmjs(this.ref); } toBinary(bufferSize: usize = 1048576): Uint8Array { @@ -785,132 +785,48 @@ export class Module { createRelooper(): Relooper { return this.noEmit ? Relooper.createStub(this) : Relooper.create(this); } -} -export function getExpressionId(expr: ExpressionRef): ExpressionId { - return _BinaryenExpressionGetId(expr); -} + // currently supports side effect free expressions only + cloneExpression(expr: ExpressionRef, noSideEffects: bool = false, maxDepth: i32 = 0x7fffffff): ExpressionRef { + if (this.noEmit || maxDepth < 0) return 0; -export function getExpressionType(expr: ExpressionRef): NativeType { - return _BinaryenExpressionGetType(expr); -} + let nested1: ExpressionRef, + nested2: ExpressionRef; -export function printExpression(expr: ExpressionRef): void { - return _BinaryenExpressionPrint(expr); -} + switch (_BinaryenExpressionGetId(expr)) { -export function getConstValueI32(expr: ExpressionRef): i32 { - return _BinaryenConstGetValueI32(expr); -} + case ExpressionId.Const: + switch (_BinaryenExpressionGetType(expr)) { + case NativeType.I32: return this.createI32(_BinaryenConstGetValueI32(expr)); + case NativeType.I64: return this.createI64(_BinaryenConstGetValueI64Low(expr), _BinaryenConstGetValueI64High(expr)); + case NativeType.F32: return this.createF32(_BinaryenConstGetValueF32(expr)); + case NativeType.F64: return this.createF64(_BinaryenConstGetValueF64(expr)); + default: throw new Error("unexpected constant type"); + } -export function getConstValueI64Low(expr: ExpressionRef): i32 { - return _BinaryenConstGetValueI64Low(expr); -} + case ExpressionId.GetLocal: + return _BinaryenGetLocal(this.ref, _BinaryenGetLocalGetIndex(expr), _BinaryenExpressionGetType(expr)); -export function getConstValueI64High(expr: ExpressionRef): i32 { - return _BinaryenConstGetValueI64High(expr); -} + // case ExpressionId.GetGlobal: explodes if it doesn't have a name + // return _BinaryenGetGlobal(this.ref, _BinaryenGetGlobalGetName(expr), _BinaryenExpressionGetType(expr)); -export function getConstValueI64(expr: ExpressionRef): I64 { - return new I64( - _BinaryenConstGetValueI64Low(expr), - _BinaryenConstGetValueI64High(expr) - ); -} + case ExpressionId.Load: + if (!(nested1 = this.cloneExpression(_BinaryenLoadGetPtr(expr), noSideEffects, maxDepth - 1))) break; + return _BinaryenLoadIsAtomic(expr) + ? _BinaryenAtomicLoad(this.ref, _BinaryenLoadGetBytes(expr), _BinaryenLoadGetOffset(expr), _BinaryenExpressionGetType(expr), nested1) + : _BinaryenLoad(this.ref, _BinaryenLoadGetBytes(expr), _BinaryenLoadIsSigned(expr) ? 1 : 0, _BinaryenLoadGetOffset(expr), _BinaryenLoadGetAlign(expr), _BinaryenExpressionGetType(expr), nested1); -export function getConstValueF32(expr: ExpressionRef): f32 { - return _BinaryenConstGetValueF32(expr); -} + case ExpressionId.Unary: + if (!(nested1 = this.cloneExpression(_BinaryenUnaryGetValue(expr), noSideEffects, maxDepth - 1))) break; + return _BinaryenUnary(this.ref, _BinaryenUnaryGetOp(expr), nested1); -export function getConstValueF64(expr: ExpressionRef): f64 { - return _BinaryenConstGetValueF64(expr); -} - -export function getGetLocalIndex(expr: ExpressionRef): Index { - return _BinaryenGetLocalGetIndex(expr); -} - -export function getGetGlobalName(expr: ExpressionRef): string | null { - return readString(_BinaryenGetGlobalGetName(expr)); -} - -export function isLoadAtomic(expr: ExpressionRef): bool { - return _BinaryenLoadIsAtomic(expr); -} - -export function isLoadSigned(expr: ExpressionRef): bool { - return _BinaryenLoadIsSigned(expr); -} - -export function getLoadBytes(expr: ExpressionRef): u32 { - return _BinaryenLoadGetBytes(expr); -} - -export function getLoadOffset(expr: ExpressionRef): u32 { - return _BinaryenLoadGetOffset(expr); -} - -export function getLoadPtr(expr: ExpressionRef): ExpressionRef { - return _BinaryenLoadGetPtr(expr); -} - -export function getFunctionBody(func: FunctionRef): ExpressionRef { - return _BinaryenFunctionGetBody(func); -} - -export function getUnaryOp(expr: ExpressionRef): UnaryOp { - return _BinaryenUnaryGetOp(expr); -} - -export function getUnaryValue(expr: ExpressionRef): ExpressionRef { - return _BinaryenUnaryGetValue(expr); -} - -export function getBinaryOp(expr: ExpressionRef): BinaryOp { - return _BinaryenBinaryGetOp(expr); -} - -export function getBinaryLeft(expr: ExpressionRef): ExpressionRef { - return _BinaryenBinaryGetLeft(expr); -} - -export function getBinaryRight(expr: ExpressionRef): ExpressionRef { - return _BinaryenBinaryGetRight(expr); -} - -export function getSelectIfTrue(expr: ExpressionRef): ExpressionRef { - return _BinaryenSelectGetIfTrue(expr); -} - -export function getSelectIfFalse(expr: ExpressionRef): ExpressionRef { - return _BinaryenSelectGetIfFalse(expr); -} - -export function getSelectCondition(expr: ExpressionRef): ExpressionRef { - return _BinaryenSelectGetCondition(expr); -} - -export function getReturnValue(expr: ExpressionRef): ExpressionRef { - return _BinaryenReturnGetValue(expr); -} - -export function getDropValue(expr: ExpressionRef): ExpressionRef { - return _BinaryenDropGetValue(expr); -} - -export function getHostOp(expr: ExpressionRef): HostOp { - return _BinaryenHostGetOp(expr); -} - -export function getHostNameOperand(expr: ExpressionRef): string | null { - return readString(_BinaryenHostGetNameOperand(expr)); -} - -export function getHostOperands(expr: ExpressionRef): BinaryenExpressionRef[] { - const num: Index = _BinaryenHostGetNumOperands(expr); - const arr: BinaryenExpressionRef[] = new Array(num); - for (let i: Index = 0; i < num; ++i) arr[i] = _BinaryenHostGetOperand(expr, i); - return arr; + case ExpressionId.Binary: + if (!(nested1 = this.cloneExpression(_BinaryenBinaryGetLeft(expr), noSideEffects, maxDepth - 1))) break; + if (!(nested2 = this.cloneExpression(_BinaryenBinaryGetRight(expr), noSideEffects, maxDepth - 1))) break; + return _BinaryenBinary(this.ref, _BinaryenBinaryGetOp(expr), nested1, nested2); + } + return 0; + } } export class Relooper { @@ -990,6 +906,7 @@ function allocI32Array(i32s: i32[] | null): usize { let idx: usize = ptr; for (let i: i32 = 0, k: i32 = (i32s).length; i < k; ++i) { let val: i32 = (i32s)[i]; + // store(idx, val) is not portable store(idx , ( val & 0xff) as u8); store(idx + 1, ((val >> 8) & 0xff) as u8); store(idx + 2, ((val >> 16) & 0xff) as u8); @@ -1022,7 +939,7 @@ function stringLengthUTF8(str: string): usize { } function allocString(str: string | null): usize { - if (!str) return 0; + if (str == null) return 0; const ptr: usize = Heap.allocate(stringLengthUTF8((str)) + 1); let idx: usize = ptr; for (let i: i32 = 0, k: i32 = (str).length; i < k; ++i) { @@ -1064,18 +981,18 @@ function allocString(str: string | null): usize { export function readString(ptr: usize): string | null { if (!ptr) return null; - const utf16le: u32[] = []; + const arr: i32[] = []; // the following is based on Emscripten's UTF8ArrayToString let cp: u32; let u1: u32, u2: u32, u3: u32, u4: u32, u5: u32; while (cp = load(ptr++)) { if (!(cp & 0x80)) { - utf16le.push(cp); + arr.push(cp); continue; } u1 = load(ptr++) & 63; if ((cp & 0xE0) == 0xC0) { - utf16le.push(((cp & 31) << 6) | u1); + arr.push(((cp & 31) << 6) | u1); continue; } u2 = load(ptr++) & 63; @@ -1095,14 +1012,14 @@ export function readString(ptr: usize): string | null { } } } - if (cp < 0x10000) { - utf16le.push(cp); - } else { - var ch = cp - 0x10000; - utf16le.push(0xD800 | (ch >> 10)); - utf16le.push(0xDC00 | (ch & 0x3FF)); - } + // if (cp < 0x10000) { + // arr.push(cp); + // } else { + // var ch = cp - 0x10000; + // arr.push(0xD800 | (ch >> 10)); + // arr.push(0xDC00 | (ch & 0x3FF)); + // } } - // FIXME: not portable and prone to stack overflows. Maybe use CString from stdlib? - return String.fromCharCode.apply(utf16le); + // return String.fromCharCodes(arr); + return String.fromCodePoints(arr); } diff --git a/src/program.ts b/src/program.ts index b8870b0c..4efa118c 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1,5 +1,5 @@ import { initialize as initializeBuiltins } from "./builtins"; -import { Target } from "./compiler"; +import { Target, typeToNativeType } from "./compiler"; import { GETTER_PREFIX, SETTER_PREFIX, PATH_DELIMITER } from "./constants"; import { DiagnosticCode, DiagnosticMessage, DiagnosticEmitter } from "./diagnostics"; import { Type, typesToString } from "./types"; @@ -45,6 +45,7 @@ import { mangleInternalName } from "./ast"; +import { NativeType } from "./module"; class QueuedExport { isReExport: bool; @@ -973,6 +974,53 @@ export class Function extends Element { return local; } + private tempI32s: Local[] = []; + private tempI64s: Local[] = []; + private tempF32s: Local[] = []; + private tempF64s: Local[] = []; + + getTempLocal(type: Type): Local { + let temps: Local[]; + switch (typeToNativeType(type)) { + case NativeType.I32: temps = this.tempI32s; break; + case NativeType.I64: temps = this.tempI64s; break; + case NativeType.F32: temps = this.tempF32s; break; + case NativeType.F64: temps = this.tempF64s; break; + default: throw new Error("unexpected type"); + } + if (temps.length > 0) + return temps.pop(); + return this.addLocal(type); + } + + freeTempLocal(local: Local): void { + let temps: Local[]; + switch (typeToNativeType(local.type)) { + case NativeType.I32: temps = this.tempI32s; break; + case NativeType.I64: temps = this.tempI64s; break; + case NativeType.F32: temps = this.tempF32s; break; + case NativeType.F64: temps = this.tempF64s; break; + default: throw new Error("unexpected type"); + } + temps.push(local); + } + + getAndFreeTempLocal(type: Type): Local { + let temps: Local[]; + switch (typeToNativeType(type)) { + case NativeType.I32: temps = this.tempI32s; break; + case NativeType.I64: temps = this.tempI64s; break; + case NativeType.F32: temps = this.tempF32s; break; + case NativeType.F64: temps = this.tempF64s; break; + default: throw new Error("unexpected type"); + } + if (temps.length > 0) + return temps[temps.length - 1]; + let local: Local = this.addLocal(type); + temps.push(local); + return local; + } + /** Enters a(nother) break context. */ enterBreakContext(): string { const id: i32 = this.nextBreakId++; diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index ce16ba57..c9577b99 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -13,36 +13,10 @@ (func $start (; 0 ;) (type $v) (local $0 i32) (local $1 i32) - (local $2 i32) - (local $3 i32) - (local $4 i32) - (local $5 i32) - (local $6 i32) - (local $7 i32) - (local $8 i32) - (local $9 i32) - (local $10 i64) - (local $11 i64) - (local $12 i64) - (local $13 i64) - (local $14 i64) - (local $15 i64) - (local $16 f32) - (local $17 f32) - (local $18 f32) - (local $19 f32) - (local $20 f64) - (local $21 f64) - (local $22 f64) - (local $23 f64) - (local $24 f32) - (local $25 f64) - (local $26 f32) - (local $27 f32) - (local $28 f64) - (local $29 f64) - (local $30 f32) - (local $31 f64) + (local $2 i64) + (local $3 i64) + (local $4 f32) + (local $5 f64) (drop (i32.clz (i32.const 1) @@ -90,29 +64,29 @@ ) (drop (select - (tee_local $1 + (tee_local $0 (i32.const 1) ) - (tee_local $2 + (tee_local $1 (i32.const 2) ) (i32.gt_s + (get_local $0) (get_local $1) - (get_local $2) ) ) ) (drop (select - (tee_local $3 + (tee_local $0 (i32.const 1) ) - (tee_local $4 + (tee_local $1 (i32.const 2) ) (i32.lt_s - (get_local $3) - (get_local $4) + (get_local $0) + (get_local $1) ) ) ) @@ -147,16 +121,16 @@ (select (i32.sub (i32.const 0) - (tee_local $5 + (tee_local $0 (i32.sub (i32.const 0) (i32.const 42) ) ) ) - (get_local $5) + (get_local $0) (i32.lt_s - (get_local $5) + (get_local $0) (i32.const 0) ) ) @@ -172,15 +146,15 @@ ) (set_global $builtins/i (select - (tee_local $6 + (tee_local $0 (i32.const 1) ) - (tee_local $7 + (tee_local $1 (i32.const 2) ) (i32.gt_s - (get_local $6) - (get_local $7) + (get_local $0) + (get_local $1) ) ) ) @@ -195,15 +169,15 @@ ) (set_global $builtins/i (select - (tee_local $8 + (tee_local $0 (i32.const 1) ) - (tee_local $9 + (tee_local $1 (i32.const 2) ) (i32.lt_s - (get_local $8) - (get_local $9) + (get_local $0) + (get_local $1) ) ) ) @@ -247,16 +221,16 @@ (select (i64.sub (i64.const 0) - (tee_local $10 + (tee_local $2 (i64.sub (i64.const 0) (i64.const 42) ) ) ) - (get_local $10) + (get_local $2) (i64.lt_s - (get_local $10) + (get_local $2) (i64.const 0) ) ) @@ -292,16 +266,16 @@ (select (i64.sub (i64.const 0) - (tee_local $11 + (tee_local $2 (i64.sub (i64.const 0) (i64.const 42) ) ) ) - (get_local $11) + (get_local $2) (i64.lt_s - (get_local $11) + (get_local $2) (i64.const 0) ) ) @@ -317,15 +291,15 @@ ) (set_global $builtins/I (select - (tee_local $12 + (tee_local $2 (i64.const 1) ) - (tee_local $13 + (tee_local $3 (i64.const 2) ) (i64.gt_s - (get_local $12) - (get_local $13) + (get_local $2) + (get_local $3) ) ) ) @@ -340,15 +314,15 @@ ) (set_global $builtins/I (select - (tee_local $14 + (tee_local $2 (i64.const 1) ) - (tee_local $15 + (tee_local $3 (i64.const 2) ) (i64.lt_s - (get_local $14) - (get_local $15) + (get_local $2) + (get_local $3) ) ) ) @@ -417,17 +391,17 @@ ) (drop (f32.ne - (tee_local $16 + (tee_local $4 (f32.const 1.25) ) - (get_local $16) + (get_local $4) ) ) (drop (select (f32.ne (f32.abs - (tee_local $17 + (tee_local $4 (f32.const 1.25) ) ) @@ -435,8 +409,8 @@ ) (i32.const 0) (f32.eq - (get_local $17) - (get_local $17) + (get_local $4) + (get_local $4) ) ) ) @@ -496,17 +470,17 @@ ) (set_global $builtins/b (f32.ne - (tee_local $18 + (tee_local $4 (f32.const 1.25) ) - (get_local $18) + (get_local $4) ) ) (set_global $builtins/b (select (f32.ne (f32.abs - (tee_local $19 + (tee_local $4 (f32.const 1.25) ) ) @@ -514,8 +488,8 @@ ) (i32.const 0) (f32.eq - (get_local $19) - (get_local $19) + (get_local $4) + (get_local $4) ) ) ) @@ -581,17 +555,17 @@ ) (drop (f64.ne - (tee_local $20 + (tee_local $5 (f64.const 1.25) ) - (get_local $20) + (get_local $5) ) ) (drop (select (f64.ne (f64.abs - (tee_local $21 + (tee_local $5 (f64.const 1.25) ) ) @@ -599,8 +573,8 @@ ) (i32.const 0) (f64.eq - (get_local $21) - (get_local $21) + (get_local $5) + (get_local $5) ) ) ) @@ -660,17 +634,17 @@ ) (set_global $builtins/b (f64.ne - (tee_local $22 + (tee_local $5 (f64.const 1.25) ) - (get_local $22) + (get_local $5) ) ) (set_global $builtins/b (select (f64.ne (f64.abs - (tee_local $23 + (tee_local $5 (f64.const 1.25) ) ) @@ -678,8 +652,8 @@ ) (i32.const 0) (f64.eq - (get_local $23) - (get_local $23) + (get_local $5) + (get_local $5) ) ) ) @@ -910,10 +884,10 @@ (if (i32.eqz (f32.ne - (tee_local $24 + (tee_local $4 (f32.const nan:0x400000) ) - (get_local $24) + (get_local $4) ) ) (unreachable) @@ -921,10 +895,10 @@ (if (i32.eqz (f64.ne - (tee_local $25 + (tee_local $5 (f64.const nan:0x8000000000000) ) - (get_local $25) + (get_local $5) ) ) (unreachable) @@ -935,7 +909,7 @@ (select (f32.ne (f32.abs - (tee_local $26 + (tee_local $4 (f32.const nan:0x400000) ) ) @@ -943,8 +917,8 @@ ) (i32.const 0) (f32.eq - (get_local $26) - (get_local $26) + (get_local $4) + (get_local $4) ) ) ) @@ -957,7 +931,7 @@ (select (f32.ne (f32.abs - (tee_local $27 + (tee_local $4 (f32.const inf) ) ) @@ -965,8 +939,8 @@ ) (i32.const 0) (f32.eq - (get_local $27) - (get_local $27) + (get_local $4) + (get_local $4) ) ) ) @@ -979,7 +953,7 @@ (select (f64.ne (f64.abs - (tee_local $28 + (tee_local $5 (f64.const nan:0x8000000000000) ) ) @@ -987,8 +961,8 @@ ) (i32.const 0) (f64.eq - (get_local $28) - (get_local $28) + (get_local $5) + (get_local $5) ) ) ) @@ -1001,7 +975,7 @@ (select (f64.ne (f64.abs - (tee_local $29 + (tee_local $5 (f64.const inf) ) ) @@ -1009,8 +983,8 @@ ) (i32.const 0) (f64.eq - (get_local $29) - (get_local $29) + (get_local $5) + (get_local $5) ) ) ) @@ -1022,7 +996,7 @@ (select (f32.ne (f32.abs - (tee_local $30 + (tee_local $4 (f32.const 0) ) ) @@ -1030,8 +1004,8 @@ ) (i32.const 0) (f32.eq - (get_local $30) - (get_local $30) + (get_local $4) + (get_local $4) ) ) ) @@ -1042,7 +1016,7 @@ (select (f64.ne (f64.abs - (tee_local $31 + (tee_local $5 (f64.const 0) ) ) @@ -1050,8 +1024,8 @@ ) (i32.const 0) (f64.eq - (get_local $31) - (get_local $31) + (get_local $5) + (get_local $5) ) ) )