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

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 {

0 commit comments

Comments
 (0)