diff --git a/README.md b/README.md index 15064e46..0bb4ca50 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,11 @@ Development status This version of the compiler (0.5.0, NEXT) is relatively new and does not yet support some features a TypeScript programmer might expect, e.g., strings, arrays and classes. For now, you can see the [compiler tests](https://github.com/AssemblyScript/next/tree/master/tests/compiler) for an overview of what's supposed to be working already. +A few early examples to get an idea: + +* [memcpy](./tests/compiler/memcpy.ts) using load/store derived from [musl](http://www.musl-libc.org) +* Conway's [Game of Life](./tests/compiler/game-of-life.ts) ([html](./tests/compiler/game-of-life.html)) as seen on [dcode.io](http://dcode.io) + Getting started --------------- @@ -25,7 +30,7 @@ $> cd next $> npm install ``` -Author your module in AssemblyScript ([definitions](./assembly.d.ts)) or portable AssemblyScript ([definitions](./portable-assembly.d.ts)) and run: +Author your module in AssemblyScript ([definitions](./assembly.d.ts), [base config](./assembly.json)) or portable AssemblyScript ([definitions](./portable.d.ts), [base config](./portable.json) and run: ``` $> node bin\asc yourModule.ts diff --git a/portable-assembly.d.ts b/portable.d.ts similarity index 99% rename from portable-assembly.d.ts rename to portable.d.ts index 1e624a11..843a5709 100644 --- a/portable-assembly.d.ts +++ b/portable.d.ts @@ -108,6 +108,7 @@ declare interface IArguments {} declare class Error { constructor(message: string); message: string; + stack: string | null; } declare class Symbol { diff --git a/portable-assembly.js b/portable.js similarity index 100% rename from portable-assembly.js rename to portable.js diff --git a/portable-assembly.json b/portable.json similarity index 86% rename from portable-assembly.json rename to portable.json index 18d3f88a..de0add81 100644 --- a/portable-assembly.json +++ b/portable.json @@ -14,8 +14,8 @@ "allowJs": true }, "files": [ - "./portable-assembly.d.ts", - "./portable-assembly.js", + "./portable.d.ts", + "./portable.js", "./std/portable/heap.d.ts", "./std/portable/heap.js" ] diff --git a/src/compiler.ts b/src/compiler.ts index f23fc8b2..6ae29ac2 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1277,9 +1277,9 @@ export class Compiler extends DiagnosticEmitter { compileAssertionExpression(expression: AssertionExpression, contextualType: Type): ExpressionRef { const toType: Type | null = this.program.resolveType(expression.toType, this.currentFunction.contextualTypeArguments); // reports - return toType && toType != contextualType - ? this.compileExpression(expression.expression, toType, ConversionKind.EXPLICIT) - : this.compileExpression(expression.expression, contextualType); + if (!toType) + return this.module.createUnreachable(); + return this.compileExpression(expression.expression, toType, ConversionKind.EXPLICIT); } compileBinaryExpression(expression: BinaryExpression, contextualType: Type): ExpressionRef { @@ -1300,9 +1300,13 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.LtF32 : this.currentType == Type.f64 ? BinaryOp.LtF64 + : this.currentType.isSignedInteger + ? this.currentType.isLongInteger + ? BinaryOp.LtI64 + : BinaryOp.LtI32 : this.currentType.isLongInteger - ? BinaryOp.LtI64 - : BinaryOp.LtI32; + ? BinaryOp.LtU64 + : BinaryOp.LtU32; this.currentType = Type.bool; break; @@ -1313,9 +1317,13 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.GtF32 : this.currentType == Type.f64 ? BinaryOp.GtF64 + : this.currentType.isSignedInteger + ? this.currentType.isLongInteger + ? BinaryOp.GtI64 + : BinaryOp.GtI32 : this.currentType.isLongInteger - ? BinaryOp.GtI64 - : BinaryOp.GtI32; + ? BinaryOp.GtU64 + : BinaryOp.GtU32; this.currentType = Type.bool; break; @@ -1326,9 +1334,13 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.LeF32 : this.currentType == Type.f64 ? BinaryOp.LeF64 + : this.currentType.isSignedInteger + ? this.currentType.isLongInteger + ? BinaryOp.LeI64 + : BinaryOp.LeI32 : this.currentType.isLongInteger - ? BinaryOp.LeI64 - : BinaryOp.LeI32; + ? BinaryOp.LeU64 + : BinaryOp.LeU32; this.currentType = Type.bool; break; @@ -1339,9 +1351,13 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.GeF32 : this.currentType == Type.f64 ? BinaryOp.GeF64 + : this.currentType.isSignedInteger + ? this.currentType.isLongInteger + ? BinaryOp.GeI64 + : BinaryOp.GeI32 : this.currentType.isLongInteger - ? BinaryOp.GeI64 - : BinaryOp.GeI32; + ? BinaryOp.GeU64 + : BinaryOp.GeU32; this.currentType = Type.bool; break; @@ -1427,9 +1443,13 @@ export class Compiler extends DiagnosticEmitter { ? BinaryOp.DivF32 : this.currentType == Type.f64 ? BinaryOp.DivF64 + : this.currentType.isSignedInteger + ? this.currentType.isLongInteger + ? BinaryOp.DivI64 + : BinaryOp.DivI32 : this.currentType.isLongInteger - ? BinaryOp.DivI64 - : BinaryOp.DivI32; + ? BinaryOp.DivU64 + : BinaryOp.DivU32; break; case Token.PERCENT_EQUALS: @@ -1439,15 +1459,19 @@ export class Compiler extends DiagnosticEmitter { right = this.compileExpression(expression.right, this.currentType); if (this.currentType.isAnyFloat) throw new Error("not implemented"); // TODO: internal fmod, possibly simply imported from JS - op = this.currentType.isLongInteger - ? BinaryOp.RemI64 - : BinaryOp.RemI32; + op = this.currentType.isSignedInteger + ? this.currentType.isLongInteger + ? BinaryOp.RemI64 + : BinaryOp.RemI32 + : this.currentType.isLongInteger + ? BinaryOp.RemU64 + : BinaryOp.RemU32; break; case Token.LESSTHAN_LESSTHAN_EQUALS: compound = Token.EQUALS; case Token.LESSTHAN_LESSTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.ShlI64 @@ -1457,7 +1481,7 @@ export class Compiler extends DiagnosticEmitter { case Token.GREATERTHAN_GREATERTHAN_EQUALS: compound = Token.EQUALS; case Token.GREATERTHAN_GREATERTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isSignedInteger ? this.currentType.isLongInteger @@ -1471,7 +1495,7 @@ export class Compiler extends DiagnosticEmitter { case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS: compound = Token.EQUALS; case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.u64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.ShrU64 @@ -1481,7 +1505,7 @@ export class Compiler extends DiagnosticEmitter { case Token.AMPERSAND_EQUALS: compound = Token.EQUALS; case Token.AMPERSAND: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.AndI64 @@ -1491,7 +1515,7 @@ export class Compiler extends DiagnosticEmitter { case Token.BAR_EQUALS: compound = Token.EQUALS; case Token.BAR: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.OrI64 @@ -1501,7 +1525,7 @@ export class Compiler extends DiagnosticEmitter { case Token.CARET_EQUALS: compound = Token.EQUALS; case Token.CARET: - left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType.isAnyFloat ? Type.i64 : contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); op = this.currentType.isLongInteger ? BinaryOp.XorI64 @@ -1509,7 +1533,7 @@ export class Compiler extends DiagnosticEmitter { break; case Token.AMPERSAND_AMPERSAND: // left && right - left = this.compileExpression(expression.left, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); 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)) @@ -1542,7 +1566,7 @@ export class Compiler extends DiagnosticEmitter { ); case Token.BAR_BAR: // left || right - left = this.compileExpression(expression.left, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT); + left = this.compileExpression(expression.left, contextualType, ConversionKind.NONE); right = this.compileExpression(expression.right, this.currentType); // simplify if left is free of side effects while tolerating two levels of nesting @@ -1782,16 +1806,15 @@ export class Compiler extends DiagnosticEmitter { // local if (element.kind == ElementKind.LOCAL) { this.currentType = (element).type; - return this.module.createGetLocal((element).index, typeToNativeType(this.currentType = (element).type)); + return this.module.createGetLocal((element).index, typeToNativeType(this.currentType)); } // global if (element.kind == ElementKind.GLOBAL) { const global: Global = element; - if (global.type) - this.currentType = global.type; if (!this.compileGlobal(global)) // reports return this.module.createUnreachable(); + this.currentType = global.type; if (global.hasConstantValue) { if (global.type == Type.f32) return this.module.createF32((element).constantFloatValue); @@ -1804,7 +1827,7 @@ export class Compiler extends DiagnosticEmitter { else throw new Error("unexpected global type"); } else - return this.module.createGetGlobal((element).internalName, typeToNativeType(this.currentType = (element).type)); + return this.module.createGetGlobal((element).internalName, typeToNativeType(this.currentType)); } // field @@ -1859,7 +1882,8 @@ export class Compiler extends DiagnosticEmitter { } compileParenthesizedExpression(expression: ParenthesizedExpression, contextualType: Type): ExpressionRef { - return this.compileExpression(expression.expression, contextualType); + // does not change types, just order + return this.compileExpression(expression.expression, contextualType, ConversionKind.NONE); } compilePropertyAccessExpression(expression: PropertyAccessExpression, contextualType: Type): ExpressionRef { diff --git a/src/decompiler.ts b/src/decompiler.ts new file mode 100644 index 00000000..dbf31e0b --- /dev/null +++ b/src/decompiler.ts @@ -0,0 +1,167 @@ +import { + Module, + NativeType, + ExpressionId, + UnaryOp, + BinaryOp, + HostOp, + FunctionTypeRef, + FunctionRef, + ExpressionRef, + + getFunctionBody, + getExpressionId, + getExpressionType, + getUnaryOp, + getUnaryValue, + getBinaryOp, + getBinaryLeft, + getBinaryRight, + getSelectIfTrue, + getSelectIfFalse, + getSelectCondition, + getHostOp, + getHostNameOperand, + getHostOperands, + getConstValueI32, + getConstValueI64Low, + getConstValueI64High, + getConstValueF32, + getConstValueF64, + getReturnValue, + getDropValue +} from "./module"; +import { I64 } from "./util"; + +// TODO :-) + +class Decompiler { + + name: string; + text: string[] = []; + + private tempI64: I64 = new I64(); + + // Decide whether to decompile to an AST or to text directly. + // AST is a bit more useful, text a lot more efficient. + + constructor(name: string = "module.ts") { + this.name = name; + } + + /** Decompiles a module to an AST that can then be serialized. */ + decompile(module: Module) { + throw new Error("not implemented"); + } + + decompileFunction(func: FunctionRef): void { + const body: ExpressionRef = getFunctionBody(func); + throw new Error("not implemented"); + } + + decompileExpression(expr: ExpressionRef): void { + const id: ExpressionId = getExpressionId(expr); + const type: NativeType = getExpressionType(expr); + + switch (id) { + case ExpressionId.Block: + case ExpressionId.If: + case ExpressionId.Loop: + case ExpressionId.Break: + case ExpressionId.Switch: + case ExpressionId.Call: + case ExpressionId.CallImport: + case ExpressionId.CallIndirect: + case ExpressionId.GetLocal: + case ExpressionId.SetLocal: + case ExpressionId.GetGlobal: + case ExpressionId.SetGlobal: + case ExpressionId.Load: + case ExpressionId.Store: + case ExpressionId.Const: + switch (type) { + case NativeType.I32: + this.text.push(getConstValueI32(expr).toString(10)); + return; + case NativeType.I64: + this.tempI64.lo = getConstValueI64Low(expr); + this.tempI64.hi = getConstValueI64High(expr); + this.text.push(this.tempI64.toString()); + return; + case NativeType.F32: + this.text.push(getConstValueF32(expr).toString(10)); + return; + case NativeType.F64: + this.text.push(getConstValueF64(expr).toString(10)); + return; + default: + throw new Error("unexpected const type"); + } + case ExpressionId.Unary: + switch (getUnaryOp(expr)) { + // TODO + } + this.decompileExpression(getUnaryValue(expr)); + return; + case ExpressionId.Binary: + this.decompileExpression(getBinaryLeft(expr)); + switch (getBinaryOp(expr)) { + // TODO + } + this.decompileExpression(getBinaryRight(expr)); + return; + case ExpressionId.Select: + this.text.push("select<"); + this.text.push(nativeTypeToType(type)); + this.text.push(">("); + this.decompileExpression(getSelectIfTrue(expr)); + this.text.push(", "); + this.decompileExpression(getSelectIfFalse(expr)); + this.text.push(", "); + this.decompileExpression(getSelectCondition(expr)); + this.text.push(");"); + return; + case ExpressionId.Drop: + this.decompileExpression(getDropValue(expr)); + this.text.push(";"); + return; + case ExpressionId.Return: + if (type == NativeType.None) { + this.text.push("return;"); + } else { + this.text.push("return "); + this.decompileExpression(getReturnValue(expr)); + this.text.push(";"); + } + return; + case ExpressionId.Host: + switch (getHostOp(expr)) { + case HostOp.CurrentMemory: + case HostOp.GrowMemory: + } + break; + case ExpressionId.Nop: + this.text.push(";"); + return; + case ExpressionId.Unreachable: + this.text.push("unreachable()"); + return; + case ExpressionId.AtomicCmpxchg: + case ExpressionId.AtomicRMW: + case ExpressionId.AtomicWait: + case ExpressionId.AtomicWake: + } + throw new Error("not implemented") + } +} + +function nativeTypeToType(type: NativeType): string { + switch (type) { + case NativeType.None: return "void"; + case NativeType.I32: return "i32"; + case NativeType.I64: return "i64"; + case NativeType.F32: return "f32"; + case NativeType.F64: return "f64"; + default: throw new Error("unexpected type"); + } +} diff --git a/src/glue/js.d.ts b/src/glue/js.d.ts deleted file mode 100644 index 8e3790eb..00000000 --- a/src/glue/js.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Raw memory accesses to Binaryen memory -declare function store(ptr: usize, val: T): void; -declare function load(ptr: usize): T; diff --git a/src/glue/js.js b/src/glue/js.js index 921f3c96..e4434643 100644 --- a/src/glue/js.js +++ b/src/glue/js.js @@ -1,4 +1,4 @@ -require("../../portable-assembly"); +require("../../portable"); // Copy Binaryen exports to global scope var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self; diff --git a/src/module.ts b/src/module.ts index d06f1efa..8c7a80bf 100644 --- a/src/module.ts +++ b/src/module.ts @@ -859,6 +859,41 @@ 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 { + 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; +} + export class Relooper { module: Module; diff --git a/src/tokenizer.ts b/src/tokenizer.ts index d9d8a435..9b5af916 100644 --- a/src/tokenizer.ts +++ b/src/tokenizer.ts @@ -306,6 +306,10 @@ export class Range { get atStart(): Range { return new Range(this.source, this.start, this.start); } get atEnd(): Range { return new Range(this.source, this.end, this.end); } + + toString(): string { + return this.source.text.substring(this.start, this.end); + } } export class Tokenizer extends DiagnosticEmitter { diff --git a/src/tsconfig.json b/src/tsconfig.json index 8014651d..66846e47 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../portable-assembly.json", + "extends": "../portable.json", "compilerOptions": { "outDir": "../out", "sourceMap": true diff --git a/std/assembly/array.ts b/std/assembly/array.ts new file mode 100644 index 00000000..7df7bd62 --- /dev/null +++ b/std/assembly/array.ts @@ -0,0 +1,36 @@ +// Multiple options: +// 1. C-like with no 'length' or 'push' +// 2. Descriptors that can be constructed from lower level arrays + +class Array { + + readonly capacity: i32; + length: i32; + ptr: usize; + + static fromPtr(ptr: usize, capacity: i32): Array { + assert(capacity >= 0); + const arr: Array = new Array(0); + store(changetype, usize>(arr), capacity); + arr.length = ptr; + arr.ptr = ptr; + return arr; + } + + constructor(capacity: i32 = 0) { + assert(capacity >= 0); + this.capacity = this.length = capacity; + if (capacity > 0) { + this.ptr = Heap.allocate(capacity); + } else { + this.ptr = 0; + } + } + + dispose(): void { + store(changetype(this), 0); + Heap.dispose(this.ptr); + this.ptr = 0; + Heap.dispose(changetype(this)); + } +} diff --git a/std/portable/heap.js b/std/portable/heap.js index 2c91c7ab..cd6829ac 100644 --- a/std/portable/heap.js +++ b/std/portable/heap.js @@ -1,6 +1,6 @@ var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self; -var HEAP = new Uint8Array(65536); +var HEAP = new Uint8Array(0); var HEAP_OFFSET = 0; Object.defineProperties(globalScope["Heap"] = { @@ -8,7 +8,7 @@ Object.defineProperties(globalScope["Heap"] = { if (!size) return 0; if (HEAP_OFFSET + size > HEAP.length) { var oldHeap = HEAP; - HEAP = new Uint8Array(Math.max(HEAP.length + size, HEAP.length * 2)); + HEAP = new Uint8Array(Math.max(65536, HEAP.length + size, HEAP.length * 2)); HEAP.set(oldHeap); } var ptr = HEAP_OFFSET; diff --git a/std/portable/tsconfig.json b/std/portable/tsconfig.json index 97751bee..9e06b840 100644 --- a/std/portable/tsconfig.json +++ b/std/portable/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "../../portable-assembly.json", + "extends": "../../portable.json", "compilerOptions": { "diagnostics": true }, diff --git a/tests/compiler/game-of-life.optimized.wast b/tests/compiler/game-of-life.optimized.wast index 3078cae6..d1cd8963 100644 --- a/tests/compiler/game-of-life.optimized.wast +++ b/tests/compiler/game-of-life.optimized.wast @@ -48,7 +48,7 @@ ) (loop $continue|0 (if - (i32.lt_s + (i32.lt_u (get_local $0) (get_global $game-of-life/h) ) @@ -81,7 +81,7 @@ ) (loop $continue|1 (if - (i32.lt_s + (i32.lt_u (get_local $1) (get_global $game-of-life/w) ) @@ -208,13 +208,13 @@ (if (if (result i32) (tee_local $3 - (i32.lt_s + (i32.lt_u (get_local $2) (i32.const 2) ) ) (get_local $3) - (i32.gt_s + (i32.gt_u (get_local $2) (i32.const 3) ) diff --git a/tests/compiler/game-of-life.wast b/tests/compiler/game-of-life.wast index c378fbdc..0705d873 100644 --- a/tests/compiler/game-of-life.wast +++ b/tests/compiler/game-of-life.wast @@ -63,7 +63,7 @@ ) (loop $continue|0 (if - (i32.lt_s + (i32.lt_u (get_local $0) (get_global $game-of-life/h) ) @@ -101,7 +101,7 @@ ) (loop $continue|1 (if - (i32.lt_s + (i32.lt_u (get_local $3) (get_global $game-of-life/w) ) @@ -237,13 +237,13 @@ (if (if (result i32) (tee_local $11 - (i32.lt_s + (i32.lt_u (get_local $8) (i32.const 2) ) ) (get_local $11) - (i32.gt_s + (i32.gt_u (get_local $8) (i32.const 3) ) diff --git a/tests/compiler/i64.optimized.wast b/tests/compiler/i64.optimized.wast new file mode 100644 index 00000000..fc53cf4a --- /dev/null +++ b/tests/compiler/i64.optimized.wast @@ -0,0 +1,301 @@ +(module + (type $iiiiv (func (param i32 i32 i32 i32))) + (global $i64/lo (mut i32) (i32.const 0)) + (global $i64/hi (mut i32) (i32.const 0)) + (memory $0 1) + (export "add" (func $i64/add)) + (export "sub" (func $i64/sub)) + (export "mul" (func $i64/mul)) + (export "div_s" (func $i64/div_s)) + (export "div_u" (func $i64/div_u)) + (export "rem_s" (func $i64/rem_s)) + (export "rem_u" (func $i64/rem_u)) + (export "memory" (memory $0)) + (func $i64/add (; 0 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (set_global $i64/lo + (i32.wrap/i64 + (tee_local $4 + (i64.add + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/sub (; 1 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (set_global $i64/lo + (i32.wrap/i64 + (tee_local $4 + (i64.sub + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/mul (; 2 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (set_global $i64/lo + (i32.wrap/i64 + (tee_local $4 + (i64.mul + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/div_s (; 3 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (set_global $i64/lo + (i32.wrap/i64 + (tee_local $4 + (i64.div_s + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/div_u (; 4 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (set_global $i64/lo + (i32.wrap/i64 + (tee_local $4 + (i64.div_u + (i64.or + (i64.extend_u/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_u/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/rem_s (; 5 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (set_global $i64/lo + (i32.wrap/i64 + (tee_local $4 + (i64.rem_s + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/rem_u (; 6 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (set_global $i64/lo + (i32.wrap/i64 + (tee_local $4 + (i64.rem_u + (i64.or + (i64.extend_u/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_u/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) +) diff --git a/tests/compiler/i64.ts b/tests/compiler/i64.ts new file mode 100644 index 00000000..fb4cc374 --- /dev/null +++ b/tests/compiler/i64.ts @@ -0,0 +1,44 @@ +let lo: u32; +let hi: u32; + +export function add(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: i64 = (loLeft | hiLeft << 32) + (loRight | hiRight << 32); + lo = ret; + hi = (ret >>> 32); +} + +export function sub(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: i64 = (loLeft | hiLeft << 32) - (loRight | hiRight << 32); + lo = ret; + hi = (ret >>> 32); +} + +export function mul(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: i64 = (loLeft | hiLeft << 32) * (loRight | hiRight << 32); + lo = ret; + hi = (ret >>> 32); +} + +export function div_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: i64 = (loLeft | hiLeft << 32) / (loRight | hiRight << 32); + lo = ret; + hi = (ret >>> 32); +} + +export function div_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: u64 = (loLeft | hiLeft << 32) / (loRight | hiRight << 32); + lo = ret; + hi = (ret >>> 32); +} + +export function rem_s(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: i64 = (loLeft | hiLeft << 32) % (loRight | hiRight << 32); + lo = ret; + hi = (ret >>> 32); +} + +export function rem_u(loLeft: u32, hiLeft: u32, loRight: u32, hiRight: u32): void { + const ret: u64 = (loLeft | hiLeft << 32) % (loRight | hiRight << 32); + lo = ret; + hi = (ret >>> 32); +} diff --git a/tests/compiler/i64.wast b/tests/compiler/i64.wast new file mode 100644 index 00000000..901224e5 --- /dev/null +++ b/tests/compiler/i64.wast @@ -0,0 +1,369 @@ +(module + (type $iiiiv (func (param i32 i32 i32 i32))) + (global $i64/lo (mut i32) (i32.const 0)) + (global $i64/hi (mut i32) (i32.const 0)) + (global $HEAP_START i32 (i32.const 4)) + (memory $0 1) + (export "add" (func $i64/add)) + (export "sub" (func $i64/sub)) + (export "mul" (func $i64/mul)) + (export "div_s" (func $i64/div_s)) + (export "div_u" (func $i64/div_u)) + (export "rem_s" (func $i64/rem_s)) + (export "rem_u" (func $i64/rem_u)) + (export "memory" (memory $0)) + (func $i64/add (; 0 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (block + (set_local $4 + (i64.add + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + (set_global $i64/lo + (i32.wrap/i64 + (get_local $4) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/sub (; 1 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (block + (set_local $4 + (i64.sub + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + (set_global $i64/lo + (i32.wrap/i64 + (get_local $4) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/mul (; 2 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (block + (set_local $4 + (i64.mul + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + (set_global $i64/lo + (i32.wrap/i64 + (get_local $4) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/div_s (; 3 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (block + (set_local $4 + (i64.div_s + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + (set_global $i64/lo + (i32.wrap/i64 + (get_local $4) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/div_u (; 4 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (block + (set_local $4 + (i64.div_u + (i64.or + (i64.extend_u/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_u/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + (set_global $i64/lo + (i32.wrap/i64 + (get_local $4) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/rem_s (; 5 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (block + (set_local $4 + (i64.rem_s + (i64.or + (i64.extend_s/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_s/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_s/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + (set_global $i64/lo + (i32.wrap/i64 + (get_local $4) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) + (func $i64/rem_u (; 6 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) + (local $4 i64) + (block + (set_local $4 + (i64.rem_u + (i64.or + (i64.extend_u/i32 + (get_local $0) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $1) + ) + (i64.const 32) + ) + ) + (i64.or + (i64.extend_u/i32 + (get_local $2) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $3) + ) + (i64.const 32) + ) + ) + ) + ) + ) + (set_global $i64/lo + (i32.wrap/i64 + (get_local $4) + ) + ) + (set_global $i64/hi + (i32.wrap/i64 + (i64.shr_u + (get_local $4) + (i64.const 32) + ) + ) + ) + ) +) +(; +[program.elements] + clz + ctz + popcnt + rotl + rotr + abs + ceil + copysign + floor + max + min + nearest + sqrt + trunc + current_memory + grow_memory + unreachable + load + store + reinterpret + select + sizeof + changetype + isNaN + isFinite + assert + i64/lo + i64/hi + i64/add + i64/sub + i64/mul + i64/div_s + i64/div_u + i64/rem_s + i64/rem_u +[program.exports] + i64/add + i64/sub + i64/mul + i64/div_s + i64/div_u + i64/rem_s + i64/rem_u +;) diff --git a/tests/compiler/memcpy.optimized.wast b/tests/compiler/memcpy.optimized.wast index dcfb3e07..c17d2aca 100644 --- a/tests/compiler/memcpy.optimized.wast +++ b/tests/compiler/memcpy.optimized.wast @@ -19,7 +19,7 @@ (tee_local $3 (get_local $2) ) - (i32.rem_s + (i32.rem_u (get_local $1) (i32.const 4) ) @@ -62,7 +62,7 @@ ) (if (i32.eqz - (i32.rem_s + (i32.rem_u (get_local $4) (i32.const 4) ) @@ -70,7 +70,7 @@ (block (loop $continue|1 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 16) ) @@ -247,7 +247,7 @@ ) ) (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 32) ) @@ -258,7 +258,7 @@ (block $tablify|0 (br_table $case0|2 $case1|2 $case2|2 $tablify|0 (i32.sub - (i32.rem_s + (i32.rem_u (get_local $4) (i32.const 4) ) @@ -353,7 +353,7 @@ ) (loop $continue|3 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 17) ) @@ -532,7 +532,7 @@ ) (loop $continue|4 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 18) ) @@ -687,7 +687,7 @@ ) (loop $continue|5 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 19) ) diff --git a/tests/compiler/memcpy.wast b/tests/compiler/memcpy.wast index 06909bdb..4e17cfd0 100644 --- a/tests/compiler/memcpy.wast +++ b/tests/compiler/memcpy.wast @@ -110,7 +110,7 @@ (tee_local $7 (get_local $2) ) - (i32.rem_s + (i32.rem_u (get_local $4) (i32.const 4) ) @@ -168,7 +168,7 @@ ) (if (i32.eq - (i32.rem_s + (i32.rem_u (get_local $3) (i32.const 4) ) @@ -178,7 +178,7 @@ (block $break|1 (loop $continue|1 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 16) ) @@ -380,7 +380,7 @@ ) ) (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 32) ) @@ -389,7 +389,7 @@ (block $case1|2 (block $case0|2 (set_local $13 - (i32.rem_s + (i32.rem_u (get_local $3) (i32.const 4) ) @@ -512,7 +512,7 @@ (block $break|3 (loop $continue|3 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 17) ) @@ -707,7 +707,7 @@ (block $break|4 (loop $continue|4 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 18) ) @@ -874,7 +874,7 @@ (block $break|5 (loop $continue|5 (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 19) ) diff --git a/tests/compiler/tlsf.optimized-inlined.wast b/tests/compiler/tlsf.optimized-inlined.wast index 3bb1b2b7..55a9fe28 100644 --- a/tests/compiler/tlsf.optimized-inlined.wast +++ b/tests/compiler/tlsf.optimized-inlined.wast @@ -32,14 +32,14 @@ ) (func $tlsf/control$set_block (; 2 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (if - (i32.ge_s + (i32.ge_u (get_local $1) (i32.const 23) ) (unreachable) ) (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 32) ) @@ -133,7 +133,7 @@ ) (loop $continue|0 (if - (i32.lt_s + (i32.lt_u (get_local $1) (i32.const 23) ) @@ -151,7 +151,7 @@ ) (block (if - (i32.ge_s + (i32.ge_u (get_local $10) (i32.const 23) ) @@ -178,7 +178,7 @@ ) (loop $continue|1 (if - (i32.lt_s + (i32.lt_u (get_local $2) (i32.const 32) ) diff --git a/tests/compiler/tlsf.optimized.wast b/tests/compiler/tlsf.optimized.wast index 89df28f2..66fcd1ad 100644 --- a/tests/compiler/tlsf.optimized.wast +++ b/tests/compiler/tlsf.optimized.wast @@ -59,7 +59,7 @@ ) (func $tlsf/control$set_sl_bitmap (; 5 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32) (if - (i32.ge_s + (i32.ge_u (get_local $1) (i32.const 23) ) @@ -81,14 +81,14 @@ ) (func $tlsf/control$set_block (; 6 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (if - (i32.ge_s + (i32.ge_u (get_local $1) (i32.const 23) ) (unreachable) ) (if - (i32.ge_s + (i32.ge_u (get_local $2) (i32.const 32) ) @@ -134,7 +134,7 @@ ) (loop $continue|0 (if - (i32.lt_s + (i32.lt_u (get_local $1) (i32.const 23) ) @@ -149,7 +149,7 @@ ) (loop $continue|1 (if - (i32.lt_s + (i32.lt_u (get_local $2) (i32.const 32) ) diff --git a/tests/compiler/tlsf.wast b/tests/compiler/tlsf.wast index 1580a8ca..2bcb0764 100644 --- a/tests/compiler/tlsf.wast +++ b/tests/compiler/tlsf.wast @@ -91,7 +91,7 @@ (func $tlsf/control$set_sl_bitmap (; 5 ;) (type $iiiv) (param $0 i32) (param $1 i32) (param $2 i32) (if (i32.eqz - (i32.lt_s + (i32.lt_u (get_local $1) (i32.const 23) ) @@ -115,7 +115,7 @@ (func $tlsf/control$set_block (; 6 ;) (type $iiiiv) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32) (if (i32.eqz - (i32.lt_s + (i32.lt_u (get_local $1) (i32.const 23) ) @@ -124,7 +124,7 @@ ) (if (i32.eqz - (i32.lt_s + (i32.lt_u (get_local $2) (i32.const 32) ) @@ -174,7 +174,7 @@ ) (loop $continue|0 (if - (i32.lt_s + (i32.lt_u (get_local $1) (i32.const 23) ) @@ -193,7 +193,7 @@ ) (loop $continue|1 (if - (i32.lt_s + (i32.lt_u (get_local $2) (i32.const 32) )