From b7030d4dea0d07b90eb4701c51875bbbc3a2e12e Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Mon, 4 Dec 2017 22:47:08 +0100 Subject: [PATCH] reinterpret and select builtins --- assembly.d.ts | 18 ++- src/compiler.ts | 58 +++++++- src/program.ts | 45 ++++--- tests/compiler/binary.wast | 8 +- tests/compiler/builtins.optimized.wast | 56 +++++--- tests/compiler/builtins.ts | 57 ++++++-- tests/compiler/builtins.wast | 176 +++++++++++++++++++------ tests/compiler/declare.wast | 8 +- tests/compiler/do.wast | 8 +- tests/compiler/export.wast | 8 +- tests/compiler/if.wast | 8 +- tests/compiler/import.wast | 8 +- tests/compiler/literals.wast | 8 +- tests/compiler/logical.wast | 8 +- tests/compiler/reexport.wast | 8 +- tests/compiler/switch.wast | 8 +- tests/compiler/ternary.wast | 8 +- tests/compiler/unary.wast | 8 +- tests/compiler/while.wast | 8 +- 19 files changed, 374 insertions(+), 140 deletions(-) diff --git a/assembly.d.ts b/assembly.d.ts index bb974a42..9d3dfa57 100644 --- a/assembly.d.ts +++ b/assembly.d.ts @@ -39,7 +39,6 @@ declare function popcnt(value: T): T; declare function rotl(value: T, shift: T): T; /** Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer. */ declare function rotr(value: T, shift: T): T; - /** Computes the absolute value of a 32-bit or 64-bit float. */ declare function abs(value: T): T; /** Performs the ceiling operation on a 32-bit or 64-bit float. */ @@ -56,11 +55,16 @@ declare function min(left: T, right: T): T; declare function nearest(value: T): T; /** Reinterprets the bits of a value of type `T1` as type `T2`. Valid reinterpretations are i32 to/from f32 and i64 to/from f64. */ declare function reinterpret(value: T1): T2; +/** Selects one of two pre-evaluated values depending on the condition. */ +declare function select(ifTrue: T, ifFalse: T, condition: bool): T; /** Calculates the square root of a 32-bit or 64-bit float. */ declare function sqrt(value: T): T; /** Rounds to the nearest integer towards zero of a 32-bit or 64-bit float. */ declare function trunc(value: T): T; - +/** Loads a value of the specified type from memory. */ +declare function load(offset: usize): T; +/** Stores a value of the specified type to memory. */ +declare function store(offset: usize, value: T): void; /** Returns the current memory size in units of pages. One page is 64kb. */ declare function current_memory(): i32; /** Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous memory size in units of pages or `-1` on failure. */ @@ -68,18 +72,12 @@ declare function grow_memory(value: i32): i32; /** Emits an unreachable operation that results in a runtime error when executed. */ declare function unreachable(): any; // sic -/** Loads a value of the specified type from memory. */ -declare function load(offset: usize): T; -/** Stores a value of the specified type to memory. */ -declare function store(offset: usize, value: T): void; -/** Determines the byte size of the specified core or class type. Compiles to a constant. */ -declare function sizeof(): usize; - /** NaN (not a number) as a 32-bit or 64-bit float depending on context. */ declare const NaN: f32 | f64; /** Positive infinity as a 32-bit or 64-bit float depending on context. */ declare const Infinity: f32 | f64; - +/** Determines the byte size of the specified core or class type. Compiles to a constant. */ +declare function sizeof(): usize; /** Tests if a 32-bit or 64-bit float is NaN. */ declare function isNaN(value: T): bool; /** Tests if a 32-bit or 64-bit float is finite, that is not NaN or +/-Infinity. */ diff --git a/src/compiler.ts b/src/compiler.ts index 2d0509f6..716e18ed 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1466,7 +1466,7 @@ export class Compiler extends DiagnosticEmitter { functionInstance = functionPrototype.instances.get(sb.join(",")); if (!functionInstance) { - let arg0: ExpressionRef, arg1: ExpressionRef; + let arg0: ExpressionRef, arg1: ExpressionRef, arg2: ExpressionRef; if (functionPrototype.internalName == "sizeof") { // no parameters this.currentType = this.options.target == Target.WASM64 ? Type.usize64 : Type.usize32; @@ -1483,11 +1483,11 @@ export class Compiler extends DiagnosticEmitter { : this.module.createI32(resolvedTypeArguments[0].byteSize); } else if (functionPrototype.internalName == "load") { - this.currentType = resolvedTypeArguments[0]; if (k != 1) { this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, expression.range, "1", k.toString()); return this.module.createUnreachable(); } + this.currentType = resolvedTypeArguments[0]; if (expression.arguments.length != 1) { this.error(DiagnosticCode.Expected_0_arguments_but_got_1, expression.range, "1", expression.arguments.length.toString()); return this.module.createUnreachable(); @@ -1517,6 +1517,60 @@ export class Compiler extends DiagnosticEmitter { if (!arg1) return this.module.createUnreachable(); return this.module.createStore(resolvedTypeArguments[0].byteSize, arg0, arg1, typeToNativeType(resolvedTypeArguments[0])); + + } else if (functionPrototype.internalName == "reinterpret") { + if (k != 2) { + this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, expression.range, "2", k.toString()); + return this.module.createUnreachable(); + } + this.currentType = resolvedTypeArguments[1]; + if (expression.arguments.length != 1) { + this.error(DiagnosticCode.Expected_0_arguments_but_got_1, expression.range, "1", expression.arguments.length.toString()); + return this.module.createUnreachable(); + } + + if (this.currentType == Type.f64) { + arg0 = this.compileExpression(expression.arguments[0], Type.i64); // reports + this.currentType = Type.f64; + return this.module.createUnary(UnaryOp.ReinterpretI64, arg0); + } + if (this.currentType == Type.f32) { + arg0 = this.compileExpression(expression.arguments[0], Type.i32); // reports + this.currentType = Type.f32; + return this.module.createUnary(UnaryOp.ReinterpretI32, arg0); + } + if (this.currentType.isLongInteger) { + arg0 = this.compileExpression(expression.arguments[0], Type.f64); // reports + this.currentType = Type.i64; + return this.module.createUnary(UnaryOp.ReinterpretF64, arg0); + } + if (this.currentType.isAnyInteger) { + arg0 = this.compileExpression(expression.arguments[0], Type.f32); // reports + this.currentType = Type.i32; + return this.module.createUnary(UnaryOp.ReinterpretF32, arg0); + } + + } else if (functionPrototype.internalName == "select") { + if (k != 1) { + this.error(DiagnosticCode.Expected_0_type_arguments_but_got_1, expression.range, "1", k.toString()); + return this.module.createUnreachable(); + } + this.currentType = resolvedTypeArguments[0]; + if (expression.arguments.length != 3) { + this.error(DiagnosticCode.Expected_0_arguments_but_got_1, expression.range, "3", expression.arguments.length.toString()); + return this.module.createUnreachable(); + } + arg0 = this.compileExpression(expression.arguments[0], this.currentType); // reports + if (!arg0) + return this.module.createUnreachable(); + arg1 = this.compileExpression(expression.arguments[1], this.currentType); // reports + if (!arg1) + return this.module.createUnreachable(); + arg2 = this.compileExpression(expression.arguments[2], Type.i32); // reports + this.currentType = resolvedTypeArguments[0]; + if (!arg2) + return this.module.createUnreachable(); + return this.module.createSelect(arg0, arg1, arg2); } this.error(DiagnosticCode.Operation_not_supported, expression.range); return this.module.createUnreachable(); diff --git a/src/program.ts b/src/program.ts index 5a0530c1..a899e451 100644 --- a/src/program.ts +++ b/src/program.ts @@ -1111,6 +1111,9 @@ export class Interface extends Class { } } +const builtinIntTypes: Type[] = [ Type.i32, Type.i64 ]; +const builtinFloatTypes: Type[] = [ Type.f32, Type.f64 ]; + function initializeBuiltins(program: Program): void { // types @@ -1134,37 +1137,37 @@ function initializeBuiltins(program: Program): void { // functions - const genericInt: Type[] = [ Type.i32, Type.i64 ]; - const genericFloat: Type[] = [ Type.f32, Type.f64 ]; const usize: Type = program.target == Target.WASM64 ? Type.usize64 : Type.usize32; - addGenericUnaryBuiltin(program, "clz", genericInt); - addGenericUnaryBuiltin(program, "ctz", genericInt); - addGenericUnaryBuiltin(program, "popcnt", genericInt); - addGenericBinaryBuiltin(program, "rotl", genericInt); - addGenericBinaryBuiltin(program, "rotr", genericInt); + addGenericUnaryBuiltin(program, "clz", builtinIntTypes); + addGenericUnaryBuiltin(program, "ctz", builtinIntTypes); + addGenericUnaryBuiltin(program, "popcnt", builtinIntTypes); + addGenericBinaryBuiltin(program, "rotl", builtinIntTypes); + addGenericBinaryBuiltin(program, "rotr", builtinIntTypes); - addGenericUnaryBuiltin(program, "abs", genericFloat); - addGenericUnaryBuiltin(program, "ceil", genericFloat); - addGenericBinaryBuiltin(program, "copysign", genericFloat); - addGenericUnaryBuiltin(program, "floor", genericFloat); - addGenericBinaryBuiltin(program, "max", genericFloat); - addGenericBinaryBuiltin(program, "min", genericFloat); - addGenericUnaryBuiltin(program, "nearest", genericFloat); - addGenericUnaryBuiltin(program, "sqrt", genericFloat); - addGenericUnaryBuiltin(program, "trunc", genericFloat); + addGenericUnaryBuiltin(program, "abs", builtinFloatTypes); + addGenericUnaryBuiltin(program, "ceil", builtinFloatTypes); + addGenericBinaryBuiltin(program, "copysign", builtinFloatTypes); + addGenericUnaryBuiltin(program, "floor", builtinFloatTypes); + addGenericBinaryBuiltin(program, "max", builtinFloatTypes); + addGenericBinaryBuiltin(program, "min", builtinFloatTypes); + addGenericUnaryBuiltin(program, "nearest", builtinFloatTypes); + addGenericUnaryBuiltin(program, "sqrt", builtinFloatTypes); + addGenericUnaryBuiltin(program, "trunc", builtinFloatTypes); addSimpleBuiltin(program, "current_memory", [], usize); addSimpleBuiltin(program, "grow_memory", [ usize ], usize); addSimpleBuiltin(program, "unreachable", [], Type.void); - addGenericUnaryTestBuiltin(program, "isNaN", genericFloat); - addGenericUnaryTestBuiltin(program, "isFinite", genericFloat); - addSimpleBuiltin(program, "assert", [ Type.bool ], Type.void); - - addGenericAnyBuiltin(program, "sizeof"); addGenericAnyBuiltin(program, "load"); addGenericAnyBuiltin(program, "store"); + addGenericAnyBuiltin(program, "reinterpret"); + addGenericAnyBuiltin(program, "select"); + + addGenericAnyBuiltin(program, "sizeof"); + addGenericUnaryTestBuiltin(program, "isNaN", builtinFloatTypes); + addGenericUnaryTestBuiltin(program, "isFinite", builtinFloatTypes); + addSimpleBuiltin(program, "assert", [ Type.bool ], Type.void); } /** Adds a simple (non-generic) builtin. */ diff --git a/tests/compiler/binary.wast b/tests/compiler/binary.wast index 9a26e7e6..8fcaf2f0 100644 --- a/tests/compiler/binary.wast +++ b/tests/compiler/binary.wast @@ -835,12 +835,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store binary/b binary/i binary/I diff --git a/tests/compiler/builtins.optimized.wast b/tests/compiler/builtins.optimized.wast index 0f9786de..1098edcc 100644 --- a/tests/compiler/builtins.optimized.wast +++ b/tests/compiler/builtins.optimized.wast @@ -159,22 +159,6 @@ ) ) ) - (drop - (current_memory) - ) - (drop - (grow_memory - (i32.const 1) - ) - ) - (set_global $builtins/s - (current_memory) - ) - (set_global $builtins/s - (grow_memory - (i32.const 1) - ) - ) (set_global $builtins/i (i32.load (i32.const 8) @@ -211,6 +195,46 @@ (i32.const 8) (get_global $builtins/F) ) + (set_global $builtins/i + (i32.const 1067450368) + ) + (set_global $builtins/f + (f32.const 3.5032461608120427e-44) + ) + (set_global $builtins/I + (i64.const 4608308318706860032) + ) + (set_global $builtins/F + (f64.const 1.24e-322) + ) + (drop + (current_memory) + ) + (drop + (grow_memory + (i32.const 1) + ) + ) + (set_global $builtins/s + (current_memory) + ) + (set_global $builtins/s + (grow_memory + (i32.const 1) + ) + ) + (set_global $builtins/i + (i32.const 10) + ) + (set_global $builtins/I + (i64.const 200) + ) + (set_global $builtins/f + (f32.const 1.25) + ) + (set_global $builtins/F + (f64.const 25) + ) (if (f32.eq (tee_local $0 diff --git a/tests/compiler/builtins.ts b/tests/compiler/builtins.ts index 5b66c599..eaebe28e 100644 --- a/tests/compiler/builtins.ts +++ b/tests/compiler/builtins.ts @@ -1,5 +1,7 @@ let b: bool; +// integer builtins + let i: i32; clz(1); @@ -28,6 +30,8 @@ I = popcnt(1); I = rotl(1, 1); I = rotr(1, 1); +// floating point builtins + let f: f32; NaN; @@ -39,7 +43,6 @@ floor(1.25); max(1.25, 2.5); min(1.25, 2.5); nearest(1.25); -// reinterpret sqrt(1.25); trunc(1.25); isNaN(1.25); @@ -54,7 +57,6 @@ f = floor(1.25); f = max(1.25, 2.5); f = min(1.25, 2.5); f = nearest(1.25); -// reinterpret f = sqrt(1.25); f = trunc(1.25); b = isNaN(1.25); @@ -73,7 +75,6 @@ floor(1.25); max(1.25, 2.5); min(1.25, 2.5); nearest(1.25); -// reinterpret sqrt(1.25); trunc(1.25); isNaN(1.25); @@ -88,12 +89,36 @@ F = floor(1.25); F = max(1.25, 2.5); F = min(1.25, 2.5); F = nearest(1.25); -// reinterpret F = sqrt(1.25); F = trunc(1.25); b = isNaN(1.25); b = isFinite(1.25); +// load and store builtins + +i = load(8); +store(8, i); +I = load(8); +store(8, I); +f = load(8); +store(8, f); +F = load(8); +store(8, F); + +// reinterpretation builtins + +reinterpret(1.25); +reinterpret(25); +reinterpret(1.25); +reinterpret(25); + +i = reinterpret(1.25); +f = reinterpret(25); +I = reinterpret(1.25); +F = reinterpret(25); + +// host builtins + let s: usize; current_memory(); @@ -101,8 +126,23 @@ grow_memory(1); s = current_memory(); s = grow_memory(1); + +// other builtins + +select(10, 20, true); +select(100, 200, false); +select(1.25, 2.5, true); +select(12.5, 25.0, false); + +i = select(10, 20, true); +I = select(100, 200, false); +f = select(1.25, 2.5, true); +F = select(12.5, 25.0, false); + if (0) unreachable(); +// AS specific builtins + assert(true); sizeof(); @@ -119,15 +159,6 @@ sizeof(); sizeof(); sizeof(); -i = load(8); -store(8, i); -I = load(8); -store(8, I); -f = load(8); -store(8, f); -F = load(8); -store(8, F); - if (NaN == NaN) unreachable(); if (!isNaN(NaN)) diff --git a/tests/compiler/builtins.wast b/tests/compiler/builtins.wast index d7b940d4..8b68ac67 100644 --- a/tests/compiler/builtins.wast +++ b/tests/compiler/builtins.wast @@ -453,6 +453,82 @@ ) ) ) + (set_global $builtins/i + (i32.load + (i32.const 8) + ) + ) + (i32.store + (i32.const 8) + (get_global $builtins/i) + ) + (set_global $builtins/I + (i64.load + (i32.const 8) + ) + ) + (i64.store + (i32.const 8) + (get_global $builtins/I) + ) + (set_global $builtins/f + (f32.load + (i32.const 8) + ) + ) + (f32.store + (i32.const 8) + (get_global $builtins/f) + ) + (set_global $builtins/F + (f64.load + (i32.const 8) + ) + ) + (f64.store + (i32.const 8) + (get_global $builtins/F) + ) + (drop + (i32.reinterpret/f32 + (f32.const 1.25) + ) + ) + (drop + (f32.reinterpret/i32 + (i32.const 25) + ) + ) + (drop + (i64.reinterpret/f64 + (f64.const 1.25) + ) + ) + (drop + (f64.reinterpret/i64 + (i64.const 25) + ) + ) + (set_global $builtins/i + (i32.reinterpret/f32 + (f32.const 1.25) + ) + ) + (set_global $builtins/f + (f32.reinterpret/i32 + (i32.const 25) + ) + ) + (set_global $builtins/I + (i64.reinterpret/f64 + (f64.const 1.25) + ) + ) + (set_global $builtins/F + (f64.reinterpret/i64 + (i64.const 25) + ) + ) (drop (current_memory) ) @@ -469,6 +545,62 @@ (i32.const 1) ) ) + (drop + (select + (i32.const 10) + (i32.const 20) + (i32.const 1) + ) + ) + (drop + (select + (i64.const 100) + (i64.const 200) + (i32.const 0) + ) + ) + (drop + (select + (f32.const 1.25) + (f32.const 2.5) + (i32.const 1) + ) + ) + (drop + (select + (f64.const 12.5) + (f64.const 25) + (i32.const 0) + ) + ) + (set_global $builtins/i + (select + (i32.const 10) + (i32.const 20) + (i32.const 1) + ) + ) + (set_global $builtins/I + (select + (i64.const 100) + (i64.const 200) + (i32.const 0) + ) + ) + (set_global $builtins/f + (select + (f32.const 1.25) + (f32.const 2.5) + (i32.const 1) + ) + ) + (set_global $builtins/F + (select + (f64.const 12.5) + (f64.const 25) + (i32.const 0) + ) + ) (if (i32.const 0) (unreachable) @@ -518,42 +650,6 @@ (drop (i32.const 8) ) - (set_global $builtins/i - (i32.load - (i32.const 8) - ) - ) - (i32.store - (i32.const 8) - (get_global $builtins/i) - ) - (set_global $builtins/I - (i64.load - (i32.const 8) - ) - ) - (i64.store - (i32.const 8) - (get_global $builtins/I) - ) - (set_global $builtins/f - (f32.load - (i32.const 8) - ) - ) - (f32.store - (i32.const 8) - (get_global $builtins/f) - ) - (set_global $builtins/F - (f64.load - (i32.const 8) - ) - ) - (f64.store - (i32.const 8) - (get_global $builtins/F) - ) (if (f64.eq (f64.const nan:0x8000000000000) @@ -649,12 +745,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store builtins/b builtins/i builtins/I diff --git a/tests/compiler/declare.wast b/tests/compiler/declare.wast index 1b3c89cd..39be5905 100644 --- a/tests/compiler/declare.wast +++ b/tests/compiler/declare.wast @@ -25,12 +25,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store declare/external [program.exports] declare/external diff --git a/tests/compiler/do.wast b/tests/compiler/do.wast index e3a41b6c..c3f4ad1c 100644 --- a/tests/compiler/do.wast +++ b/tests/compiler/do.wast @@ -74,12 +74,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store do/loopDo do/loopDoInDo [program.exports] diff --git a/tests/compiler/export.wast b/tests/compiler/export.wast index 546bd965..dd472aff 100644 --- a/tests/compiler/export.wast +++ b/tests/compiler/export.wast @@ -43,12 +43,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store export/add export/sub export/a diff --git a/tests/compiler/if.wast b/tests/compiler/if.wast index cccbdc97..230086ce 100644 --- a/tests/compiler/if.wast +++ b/tests/compiler/if.wast @@ -65,12 +65,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store if/ifThenElse if/ifThen if/ifThenElseBlock diff --git a/tests/compiler/import.wast b/tests/compiler/import.wast index 1481eccc..c0840368 100644 --- a/tests/compiler/import.wast +++ b/tests/compiler/import.wast @@ -57,12 +57,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store export/add export/sub export/a diff --git a/tests/compiler/literals.wast b/tests/compiler/literals.wast index 776d19ad..b0c390b8 100644 --- a/tests/compiler/literals.wast +++ b/tests/compiler/literals.wast @@ -158,12 +158,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store [program.exports] ;) diff --git a/tests/compiler/logical.wast b/tests/compiler/logical.wast index d2b8f5e4..090776e2 100644 --- a/tests/compiler/logical.wast +++ b/tests/compiler/logical.wast @@ -270,12 +270,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store logical/i logical/I logical/f diff --git a/tests/compiler/reexport.wast b/tests/compiler/reexport.wast index 3ac882ef..b940dabb 100644 --- a/tests/compiler/reexport.wast +++ b/tests/compiler/reexport.wast @@ -59,12 +59,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store export/add export/sub export/a diff --git a/tests/compiler/switch.wast b/tests/compiler/switch.wast index 7704c19e..9b26fbc9 100644 --- a/tests/compiler/switch.wast +++ b/tests/compiler/switch.wast @@ -164,12 +164,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store switch/doSwitch switch/doSwitchDefaultFirst switch/doSwitchDefaultOmitted diff --git a/tests/compiler/ternary.wast b/tests/compiler/ternary.wast index 99f93858..96b255b7 100644 --- a/tests/compiler/ternary.wast +++ b/tests/compiler/ternary.wast @@ -52,12 +52,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store ternary/a [program.exports] diff --git a/tests/compiler/unary.wast b/tests/compiler/unary.wast index f79cbe41..716a93c8 100644 --- a/tests/compiler/unary.wast +++ b/tests/compiler/unary.wast @@ -652,12 +652,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store unary/i unary/I unary/f diff --git a/tests/compiler/while.wast b/tests/compiler/while.wast index daa4a241..25863f99 100644 --- a/tests/compiler/while.wast +++ b/tests/compiler/while.wast @@ -83,12 +83,14 @@ current_memory grow_memory unreachable + load + store + reinterpret + select + sizeof isNaN isFinite assert - sizeof - load - store while/loopWhile while/loopWhileInWhile [program.exports]