Skip to content

Commit 0ebb99a

Browse files
committed
Extract portable AS to its own definition and polyfill; Try running flatten/ssa before default optimizations, see WebAssembly/binaryen#1331
1 parent d6b94d4 commit 0ebb99a

23 files changed

+1415
-1131
lines changed

assembly.d.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// types
1+
// Definitions for the "AssemblyScript" subset.
2+
3+
// Types
24

35
/** An 8-bit signed integer. */
46
declare type i8 = number;
@@ -27,7 +29,7 @@ declare type f32 = number;
2729
/** A 64-bit float. */
2830
declare type f64 = number;
2931

30-
// built-ins
32+
// Built-ins
3133

3234
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
3335
declare function clz<T = i32 | i64>(value: T): T;
@@ -84,10 +86,10 @@ declare function changetype<T1,T2>(value: T1): T2;
8486
declare function isNaN<T = f32 | f64>(value: T): bool;
8587
/** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */
8688
declare function isFinite<T = f32 | f64>(value: T): bool;
87-
/** Traps if the specified value is `false`. */
89+
/** Traps if the specified value evaluates to `false`. */
8890
declare function assert(isTrue: bool): void;
8991

90-
// internal decorators
92+
// Internal decorators
9193

9294
/** Annotates an element being part of the global namespace. */
9395
declare function global(): any;
@@ -96,7 +98,7 @@ declare function inline(): any;
9698
/** Annotates a class using a C-style memory layout. */
9799
declare function struct(): any;
98100

99-
// standard library
101+
// Standard library
100102

101103
/// <reference path="./std/carray.d.ts" />
102104
/// <reference path="./std/cstring.d.ts" />

portable-assembly.d.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Definitions for the "portable AssemblyScript" subset.
2+
3+
// Portable types
4+
5+
// Note that semantics differences require additional explicit conversions for full compatibility.
6+
// For example, when casting an i32 to an u8, doing `<u8>(someI32 & 0xff)` will yield the same
7+
// result when compiling to WebAssembly or JS while `<u8>someI32` alone does nothing in JS.
8+
9+
// Note that i64's are not portable (JS numbers are IEEE754 doubles with a maximum safe integer value
10+
// of 2^53-1) and instead require a compatibility layer to work in JS as well. See: src/util/i64.ts
11+
12+
declare type i8 = number;
13+
declare type u8 = number;
14+
declare type i16 = number;
15+
declare type u16 = number;
16+
declare type i32 = number;
17+
declare type u32 = number;
18+
declare type isize = number;
19+
declare type usize = number;
20+
declare type f32 = number;
21+
declare type f64 = number;
22+
declare type bool = boolean;
23+
24+
// Portable built-ins
25+
26+
/** Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero. */
27+
declare function clz<T = i32>(value: T): T;
28+
/** Computes the absolute value of an integer or float. */
29+
declare function abs<T = i32 | f32 | f64>(value: T): T;
30+
/** Determines the maximum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
31+
declare function max<T = i32 | f32 | f64>(left: T, right: T): T;
32+
/** Determines the minimum of two integers or floats. If either operand is `NaN`, returns `NaN`. */
33+
declare function min<T = i32 | f32 | f64>(left: T, right: T): T;
34+
/** Performs the ceiling operation on a 32-bit or 64-bit float. */
35+
declare function ceil<T = f32 | f64>(value: T): T;
36+
/** Performs the floor operation on a 32-bit or 64-bit float. */
37+
declare function floor<T = f32 | f64>(value: T): T;
38+
/** Selects one of two pre-evaluated values depending on the condition. */
39+
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;
40+
/** Calculates the square root of a 32-bit or 64-bit float. */
41+
declare function sqrt<T = f32 | f64>(value: T): T;
42+
/** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */
43+
declare function trunc<T = f32 | f64>(value: T): T;
44+
/** Emits an unreachable operation that results in a runtime error when executed. */
45+
declare function unreachable(): any; // sic
46+
47+
/** Traps if the specified value evaluates to `false`. */
48+
declare function assert(isTrue: bool): void;

portable-assembly.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self;
2+
3+
globalScope["clz"] = Math.clz32;
4+
globalScope["abs"] = Math.abs;
5+
globalScope["max"] = Math.max;
6+
globalScope["min"] = Math.min;
7+
globalScope["ceil"] = Math.ceil;
8+
globalScope["floor"] = Math.floor;
9+
globalScope["select"] = function select(ifTrue, ifFalse, condition) { return condition ? ifTrue : ifFalse; };
10+
globalScope["sqrt"] = Math.sqrt;
11+
globalScope["trunc"] = Math.trunc;
12+
13+
function UnreachableError() {
14+
this.stack = new Error().stack;
15+
}
16+
UnreachableError.prototype = new Error;
17+
UnreachableError.prototype.name = "UnreachableError";
18+
UnreachableError.prototype.message = "unreachable";
19+
20+
globalScope["unreachable"] = function unreachable() { throw new UnreachableError(); };
21+
22+
function AssertionError() {
23+
this.stack = new Error().stack;
24+
}
25+
AssertionError.prototype = new Error;
26+
AssertionError.prototype.name = "AssertionError";
27+
AssertionError.prototype.message = "assertion failed";
28+
29+
globalScope["assert"] = function assert(isTrue) { if (!isTrue) throw new AssertionError(); };

src/compiler.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,7 @@ export class Compiler extends DiagnosticEmitter {
14241424
case Token.AMPERSAND_AMPERSAND: // left && right
14251425
left = this.compileExpression(expression.left, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
14261426
right = this.compileExpression(expression.right, this.currentType);
1427+
// TODO: once it's possible to clone 'left', we could check if it is a Const, GetLocal, GetGlobal or Load and avoid the tempLocal
14271428
tempLocal = this.currentFunction.addLocal(this.currentType);
14281429
return this.module.createIf(
14291430
this.currentType.isLongInteger
@@ -1440,6 +1441,7 @@ export class Compiler extends DiagnosticEmitter {
14401441
case Token.BAR_BAR: // left || right
14411442
left = this.compileExpression(expression.left, contextualType, contextualType == Type.void ? ConversionKind.NONE : ConversionKind.IMPLICIT);
14421443
right = this.compileExpression(expression.right, this.currentType);
1444+
// TODO: same as above
14431445
tempLocal = this.currentFunction.addLocal(this.currentType);
14441446
return this.module.createIf(
14451447
this.currentType.isLongInteger
File renamed without changes.

src/glue/js.d.ts

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
1-
// Aliased AssemblyScript types. Beware of semantic differences.
2-
declare type i8 = number;
3-
declare type u8 = number;
4-
declare type i16 = number;
5-
declare type u16 = number;
6-
declare type i32 = number;
7-
declare type u32 = number;
8-
declare type isize = number;
9-
declare type usize = number;
10-
declare type f32 = number;
11-
declare type f64 = number;
12-
declare type bool = boolean;
1+
/// <reference path="../../portable-assembly.d.ts" />
2+
/// <reference path="./binaryen-c.d.ts" />
133

14-
// Raw memory access (here: Binaryen memory)
4+
// Raw memory accesses to Binaryen memory
155
declare function store<T = u8>(ptr: usize, val: T): void;
166
declare function load<T = u8>(ptr: usize): T;
17-
declare function assert(isTrue: bool): void;
18-
19-
// Other things that might or might not be useful
20-
declare function select<T>(ifTrue: T, ifFalse: T, condition: bool): T;

src/glue/js.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
require("../../portable-assembly");
2+
3+
var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self;
4+
5+
var binaryen;
6+
try {
7+
binaryen = require("binaryen");
8+
} catch (e) {
9+
binaryen = globalScope["Binaryen"];
10+
}
11+
for (var key in binaryen)
12+
if (/^_(?:Binaryen|Relooper|malloc$|free$)/.test(key))
13+
globalScope[key] = binaryen[key];
14+
15+
globalScope["store"] = function store(ptr, val) {
16+
binaryen.HEAPU8[ptr] = val;
17+
};
18+
19+
globalScope["load"] = function load_u8(ptr) {
20+
return binaryen.HEAPU8[ptr];
21+
};
22+
23+
var Module = require("../module").Module;
24+
25+
Module.prototype.toBinary = function toBinary(bufferSize) {
26+
if (!bufferSize) bufferSize = 1024 * 1024;
27+
var ptr = _malloc(bufferSize);
28+
var len = this.write(ptr, bufferSize);
29+
var ret = new Uint8Array(len);
30+
ret.set(binaryen.HEAPU8.subarray(ptr, ptr + len));
31+
_free(ptr);
32+
return ret;
33+
}
34+
35+
Module.prototype.toText = function toText() {
36+
var previousPrint = binaryen.print;
37+
var ret = "";
38+
binaryen.print = function print(x) { ret += x + "\n" };
39+
this.print();
40+
binaryen.print = previousPrint;
41+
return ret;
42+
}

src/glue/js.ts

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/glue/wasm.ts

Whitespace-only changes.

src/module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -701,10 +701,13 @@ export class Module {
701701
}
702702

703703
optimize(func: FunctionRef = 0): void {
704-
if (func)
704+
// see: https://github.com/WebAssembly/binaryen/issues/1331#issuecomment-350328175
705+
this.runPasses([ "flatten", "ssa" ], func);
706+
if (func) {
705707
_BinaryenFunctionOptimize(func, this.ref);
706-
else
708+
} else {
707709
_BinaryenModuleOptimize(this.ref);
710+
}
708711
}
709712

710713
runPasses(passes: string[], func: FunctionRef = 0): void {

src/tsconfig.json

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@
2727
"diagnosticMessages.generated.ts",
2828
"diagnostics.ts",
2929
"evaluator.ts",
30-
"glue/binaryen.d.ts",
3130
"glue/js.d.ts",
32-
"glue/js.ts",
3331
"index.ts",
3432
"module.ts",
3533
"parser.ts",
@@ -43,11 +41,7 @@
4341
],
4442
"assembly": {
4543
"exclude": [
46-
"glue/js.d.ts",
47-
"glue/js.ts"
48-
],
49-
"include": [
50-
"glue/wasm.ts"
44+
"glue/js.d.ts"
5145
]
5246
}
5347
}

std/impl/heap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class Heap {
4242
let w: u32, x: u32;
4343

4444
// copy 1 byte each until src is aligned to 4 bytes
45-
while (n != 0 && src % 4 != 0) {
45+
while (n && src % 4) {
4646
store<u8>(dst++, load<u8>(src++));
4747
n--;
4848
}

tests/binaryen.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/// <reference path="../src/glue/binaryen.d.ts" />
1+
/// <reference path="../lib/binaryen.d.ts" />
22

33
import "../src/glue/js";
44
import { NativeType, Module, MemorySegment, BinaryOp } from "../src/module";
@@ -19,13 +19,13 @@ mod.addFunction("add", add, [], mod.createReturn(
1919
mod.createGetLocal(1, NativeType.I32)
2020
)
2121
));
22-
mod.addExport("add", "add");
22+
mod.addFunctionExport("add", "add");
2323

2424
const lit = mod.addFunctionType("I", NativeType.I64, []);
2525
mod.addFunction("lit", lit, [], mod.createReturn(
2626
mod.createI64(0, 0x80000000) // I64_MIN
2727
));
28-
mod.addExport("lit", "lit");
28+
mod.addFunctionExport("lit", "lit");
2929

3030
mod.addGlobal("42", NativeType.I32, false, mod.createI32(42));
3131

@@ -42,7 +42,7 @@ mod.addFunction("aSwitch", aSwitch, [ NativeType.I32 ], mod.createBlock(null, [
4242
rl.renderAndDispose(b0, 1),
4343
mod.createUnreachable()
4444
]));
45-
mod.addExport("aSwitch", "aSwitch");
45+
mod.addFunctionExport("aSwitch", "aSwitch");
4646

4747
// mod.optimize();
4848
if (mod.validate())

tests/compiler.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/// <reference path="../src/glue/binaryen.d.ts" />
2-
31
import * as fs from "fs";
42
import * as path from "path";
53
import * as chalk from "chalk";

tests/compiler/do.optimized.wast

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,47 @@
55
(export "loopDoInDo" (func $do/loopDoInDo))
66
(export "memory" (memory $0))
77
(func $do/loopDo (; 0 ;) (type $iv) (param $0 i32)
8+
(local $1 i32)
9+
(set_local $1
10+
(get_local $0)
11+
)
812
(loop $continue|0
913
(br_if $continue|0
10-
(tee_local $0
14+
(tee_local $1
1115
(i32.sub
12-
(get_local $0)
16+
(get_local $1)
1317
(i32.const 1)
1418
)
1519
)
1620
)
1721
)
1822
)
1923
(func $do/loopDoInDo (; 1 ;) (type $iv) (param $0 i32)
24+
(local $1 i32)
25+
(local $2 i32)
2026
(loop $continue|0
21-
(set_local $0
27+
(set_local $1
2228
(i32.sub
2329
(get_local $0)
2430
(i32.const 1)
2531
)
2632
)
2733
(loop $continue|1
2834
(br_if $continue|1
29-
(tee_local $0
30-
(i32.sub
31-
(get_local $0)
32-
(i32.const 1)
35+
(tee_local $2
36+
(tee_local $1
37+
(tee_local $0
38+
(i32.sub
39+
(get_local $1)
40+
(i32.const 1)
41+
)
42+
)
3343
)
3444
)
3545
)
3646
)
3747
(br_if $continue|0
38-
(get_local $0)
48+
(get_local $2)
3949
)
4050
)
4151
)

0 commit comments

Comments
 (0)