diff --git a/src/compiler.ts b/src/compiler.ts index bcd7b1b4..5cea5c78 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -2088,6 +2088,7 @@ export class Compiler extends DiagnosticEmitter { ); default: + this.error(DiagnosticCode.Operation_not_supported, expression.range); throw new Error("not implemented"); } if (compound) { diff --git a/std/portable.js b/std/portable.js index db7b8deb..415db249 100644 --- a/std/portable.js +++ b/std/portable.js @@ -45,6 +45,8 @@ Object.defineProperties( Object.defineProperties( globalScope["f32"] = function f32(value) { return Math.fround(value); } , { + "MIN_VALUE": { value: Math.fround(-3.40282347e+38), writable: false }, + "MAX_VALUE": { value: Math.fround(3.40282347e+38), writable: false }, "MIN_SAFE_INTEGER": { value: -16777215, writable: false }, "MAX_SAFE_INTEGER": { value: 16777215, writable: false }, "EPSILON": { value: Math.fround(1.19209290e-07), writable: false } @@ -52,6 +54,8 @@ Object.defineProperties( Object.defineProperties( globalScope["f64"] = function f64(value) { return +value; } , { + "MIN_VALUE": { value: -1.7976931348623157e+308, writable: false }, + "MAX_VALUE": { value: 1.7976931348623157e+308, writable: false }, "MIN_SAFE_INTEGER": { value: -9007199254740991, writable: false }, "MAX_SAFE_INTEGER": { value: 9007199254740991, writable: false }, "EPSILON": { value: 2.2204460492503131e-16, writable: false } diff --git a/tests/compiler/fmod.optimized.wast b/tests/compiler/fmod.optimized.wast new file mode 100644 index 00000000..25ec00a2 --- /dev/null +++ b/tests/compiler/fmod.optimized.wast @@ -0,0 +1,449 @@ +(module + (type $FFF (func (param f64 f64) (result f64))) + (type $v (func)) + (memory $0 1) + (export "fmod" (func $fmod/fmod)) + (export "memory" (memory $0)) + (start $start) + (func $fmod/fmod (; 0 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64) + (local $2 i64) + (local $3 i32) + (local $4 i64) + (local $5 i64) + (local $6 i32) + (local $7 i32) + (local $8 i32) + (local $9 f64) + (set_local $3 + (i32.wrap/i64 + (i64.and + (i64.shr_u + (tee_local $2 + (i64.reinterpret/f64 + (get_local $0) + ) + ) + (i64.const 52) + ) + (i64.const 2047) + ) + ) + ) + (set_local $6 + (i32.wrap/i64 + (i64.and + (i64.shr_u + (tee_local $5 + (i64.reinterpret/f64 + (get_local $1) + ) + ) + (i64.const 52) + ) + (i64.const 2047) + ) + ) + ) + (set_local $8 + (i32.wrap/i64 + (i64.shr_u + (get_local $2) + (i64.const 63) + ) + ) + ) + (if + (if (result i32) + (if (result i32) + (tee_local $7 + (i64.eq + (i64.shl + (get_local $5) + (i64.const 1) + ) + (i64.const 0) + ) + ) + (get_local $7) + (tee_local $7 + (f64.ne + (tee_local $9 + (get_local $1) + ) + (get_local $9) + ) + ) + ) + (get_local $7) + (i32.eq + (get_local $3) + (i32.const 2047) + ) + ) + (return + (f64.div + (f64.mul + (get_local $0) + (get_local $1) + ) + (f64.mul + (get_local $0) + (get_local $1) + ) + ) + ) + ) + (if + (i64.le_u + (i64.shl + (get_local $2) + (i64.const 1) + ) + (i64.shl + (get_local $5) + (i64.const 1) + ) + ) + (block + (if + (i64.eq + (i64.shl + (get_local $2) + (i64.const 1) + ) + (i64.shl + (get_local $5) + (i64.const 1) + ) + ) + (return + (f64.mul + (f64.const 0) + (get_local $0) + ) + ) + ) + (return + (get_local $0) + ) + ) + ) + (set_local $2 + (if (result i64) + (get_local $3) + (i64.or + (i64.and + (get_local $2) + (i64.const 4503599627370495) + ) + (i64.const 4503599627370496) + ) + (block (result i64) + (set_local $4 + (i64.shl + (get_local $2) + (i64.const 12) + ) + ) + (loop $continue|0 + (if + (i64.eqz + (i64.shr_u + (get_local $4) + (i64.const 63) + ) + ) + (block + (set_local $4 + (i64.shl + (get_local $4) + (i64.const 1) + ) + ) + (set_local $3 + (i32.sub + (get_local $3) + (i32.const 1) + ) + ) + (br $continue|0) + ) + ) + ) + (i64.shl + (get_local $2) + (i64.add + (i64.sub + (i64.const 0) + (i64.extend_u/i32 + (get_local $3) + ) + ) + (i64.const 1) + ) + ) + ) + ) + ) + (set_local $5 + (if (result i64) + (get_local $6) + (i64.or + (i64.and + (get_local $5) + (i64.const 4503599627370495) + ) + (i64.const 4503599627370496) + ) + (block (result i64) + (set_local $4 + (i64.shl + (get_local $5) + (i64.const 12) + ) + ) + (loop $continue|1 + (if + (i64.eqz + (i64.shr_u + (get_local $4) + (i64.const 63) + ) + ) + (block + (set_local $4 + (i64.shl + (get_local $4) + (i64.const 1) + ) + ) + (set_local $6 + (i32.sub + (get_local $6) + (i32.const 1) + ) + ) + (br $continue|1) + ) + ) + ) + (i64.shl + (get_local $5) + (i64.add + (i64.sub + (i64.const 0) + (i64.extend_u/i32 + (get_local $6) + ) + ) + (i64.const 1) + ) + ) + ) + ) + ) + (loop $continue|2 + (if + (i32.gt_s + (get_local $3) + (get_local $6) + ) + (block + (if + (i64.eqz + (i64.shr_u + (tee_local $4 + (i64.sub + (get_local $2) + (get_local $5) + ) + ) + (i64.const 63) + ) + ) + (block + (if + (i64.eqz + (get_local $4) + ) + (return + (f64.mul + (f64.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $4) + ) + ) + ) + (set_local $2 + (i64.shl + (get_local $2) + (i64.const 1) + ) + ) + (set_local $3 + (i32.sub + (get_local $3) + (i32.const 1) + ) + ) + (br $continue|2) + ) + ) + ) + (if + (i64.eqz + (i64.shr_u + (tee_local $4 + (i64.sub + (get_local $2) + (get_local $5) + ) + ) + (i64.const 63) + ) + ) + (block + (if + (i64.eqz + (get_local $4) + ) + (return + (f64.mul + (f64.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $4) + ) + ) + ) + (loop $continue|3 + (if + (i64.eqz + (i64.shr_u + (get_local $2) + (i64.const 52) + ) + ) + (block + (set_local $2 + (i64.shl + (get_local $2) + (i64.const 1) + ) + ) + (set_local $3 + (i32.sub + (get_local $3) + (i32.const 1) + ) + ) + (br $continue|3) + ) + ) + ) + (f64.reinterpret/i64 + (i64.or + (tee_local $2 + (if (result i64) + (i32.gt_s + (get_local $3) + (i32.const 0) + ) + (i64.or + (i64.sub + (get_local $2) + (i64.const 4503599627370496) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $3) + ) + (i64.const 52) + ) + ) + (i64.shr_u + (get_local $2) + (i64.add + (i64.sub + (i64.const 0) + (i64.extend_u/i32 + (get_local $3) + ) + ) + (i64.const 1) + ) + ) + ) + ) + (i64.shl + (i64.extend_u/i32 + (get_local $8) + ) + (i64.const 63) + ) + ) + ) + ) + (func $start (; 1 ;) (type $v) + (local $0 f64) + (if + (f64.eq + (tee_local $0 + (call $fmod/fmod + (f64.const 1) + (f64.const nan:0x8000000000000) + ) + ) + (get_local $0) + ) + (unreachable) + ) + (if + (f64.ne + (call $fmod/fmod + (f64.const 1.5) + (f64.const 1) + ) + (f64.const 0.5) + ) + (unreachable) + ) + (if + (i32.eqz + (f64.lt + (f64.sub + (call $fmod/fmod + (f64.const 9.2) + (f64.const 2) + ) + (f64.const 1.2) + ) + (f64.const 2.220446049250313e-16) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f64.lt + (f64.sub + (call $fmod/fmod + (f64.const 9.2) + (f64.const 3.7) + ) + (f64.const 1.8) + ) + (f64.const 2.220446049250313e-16) + ) + ) + (unreachable) + ) + ) +) diff --git a/tests/compiler/fmod.ts b/tests/compiler/fmod.ts new file mode 100644 index 00000000..8f2cf74f --- /dev/null +++ b/tests/compiler/fmod.ts @@ -0,0 +1,62 @@ +export function fmod(x: f64, y: f64): f64 { + // the following is based on musl's implementation of fmod + var ux = reinterpret(x); + var uy = reinterpret(y); + var ex = (ux >> 52 & 0x7ff); + var ey = (uy >> 52 & 0x7ff); + var sx = (ux >> 63); + + if (uy << 1 == 0 || isNaN(y) || ex == 0x7ff) + return (x * y) / (x * y); + if (ux << 1 <= uy << 1) { + if (ux << 1 == uy << 1) + return 0 * x; + return x; + } + + if (!ex) { + for (var i = ux << 12; !(i >> 63); ex--) i <<= 1; + ux <<= -ex + 1; + } else { + ux &= -1 >> 12; + ux |= 1 << 52; + } + if (!ey) { + for (i = uy << 12; !(i >> 63); ey--) i <<= 1; + uy <<= -ey + 1; + } else { + uy &= -1 >> 12; + uy |= 1 << 52; + } + + for (; ex > ey; ex--) { + i = ux - uy; + if (!(i >> 63)) { + if (!i) + return 0 * x; + ux = i; + } + ux <<= 1; + } + i = ux - uy; + if (!(i >> 63)) { + if (!i) + return 0 * x; + ux = i; + } + for (; !(ux >> 52); ex--) ux <<= 1; + + if (ex > 0) { + ux -= 1 << 52; + ux |= ex << 52; + } else { + ux >>= -ex + 1; + } + ux |= sx << 63; + return reinterpret(ux); +} + +assert(isNaN(fmod(1, NaN))); +assert(fmod(1.5, 1.0) == 0.5); // exactly 0.5 (as in C) +assert(fmod(9.2, 2.0) - 1.2 < f64.EPSILON); // not exactly 1.2 (as in C) +assert(fmod(9.2, 3.7) - 1.8 < f64.EPSILON); // not exactly 1.8 (as in C) diff --git a/tests/compiler/fmod.wast b/tests/compiler/fmod.wast new file mode 100644 index 00000000..01078801 --- /dev/null +++ b/tests/compiler/fmod.wast @@ -0,0 +1,617 @@ +(module + (type $FFF (func (param f64 f64) (result f64))) + (type $v (func)) + (global $f64.EPSILON f64 (f64.const 2.220446049250313e-16)) + (global $HEAP_BASE i32 (i32.const 4)) + (memory $0 1) + (export "fmod" (func $fmod/fmod)) + (export "memory" (memory $0)) + (start $start) + (func $fmod/fmod (; 0 ;) (type $FFF) (param $0 f64) (param $1 f64) (result f64) + (local $2 i64) + (local $3 i64) + (local $4 i32) + (local $5 i32) + (local $6 i32) + (local $7 f64) + (local $8 i32) + (local $9 i64) + (block + (set_local $2 + (i64.reinterpret/f64 + (get_local $0) + ) + ) + ) + (block + (set_local $3 + (i64.reinterpret/f64 + (get_local $1) + ) + ) + ) + (block + (set_local $4 + (i32.wrap/i64 + (i64.and + (i64.shr_u + (get_local $2) + (i64.const 52) + ) + (i64.const 2047) + ) + ) + ) + ) + (block + (set_local $5 + (i32.wrap/i64 + (i64.and + (i64.shr_u + (get_local $3) + (i64.const 52) + ) + (i64.const 2047) + ) + ) + ) + ) + (block + (set_local $6 + (i32.wrap/i64 + (i64.shr_u + (get_local $2) + (i64.const 63) + ) + ) + ) + ) + (if + (if (result i32) + (tee_local $8 + (if (result i32) + (tee_local $8 + (i64.eq + (i64.shl + (get_local $3) + (i64.const 1) + ) + (i64.const 0) + ) + ) + (get_local $8) + (f64.ne + (tee_local $7 + (get_local $1) + ) + (get_local $7) + ) + ) + ) + (get_local $8) + (i32.eq + (get_local $4) + (i32.const 2047) + ) + ) + (return + (f64.div + (f64.mul + (get_local $0) + (get_local $1) + ) + (f64.mul + (get_local $0) + (get_local $1) + ) + ) + ) + ) + (if + (i64.le_u + (i64.shl + (get_local $2) + (i64.const 1) + ) + (i64.shl + (get_local $3) + (i64.const 1) + ) + ) + (block + (if + (i64.eq + (i64.shl + (get_local $2) + (i64.const 1) + ) + (i64.shl + (get_local $3) + (i64.const 1) + ) + ) + (return + (f64.mul + (f64.const 0) + (get_local $0) + ) + ) + ) + (return + (get_local $0) + ) + ) + ) + (if + (i32.eqz + (get_local $4) + ) + (block + (block $break|0 + (block + (set_local $9 + (i64.shl + (get_local $2) + (i64.const 12) + ) + ) + ) + (loop $continue|0 + (if + (i64.eqz + (i64.shr_u + (get_local $9) + (i64.const 63) + ) + ) + (block + (set_local $9 + (i64.shl + (get_local $9) + (i64.const 1) + ) + ) + (drop + (block (result i32) + (set_local $8 + (get_local $4) + ) + (set_local $4 + (i32.sub + (get_local $8) + (i32.const 1) + ) + ) + (get_local $8) + ) + ) + (br $continue|0) + ) + ) + ) + ) + (set_local $2 + (i64.shl + (get_local $2) + (i64.add + (i64.sub + (i64.const 0) + (i64.extend_u/i32 + (get_local $4) + ) + ) + (i64.const 1) + ) + ) + ) + ) + (block + (set_local $2 + (i64.and + (get_local $2) + (i64.shr_u + (i64.sub + (i64.const 0) + (i64.const 1) + ) + (i64.const 12) + ) + ) + ) + (set_local $2 + (i64.or + (get_local $2) + (i64.shl + (i64.const 1) + (i64.const 52) + ) + ) + ) + ) + ) + (if + (i32.eqz + (get_local $5) + ) + (block + (block $break|1 + (set_local $9 + (i64.shl + (get_local $3) + (i64.const 12) + ) + ) + (loop $continue|1 + (if + (i64.eqz + (i64.shr_u + (get_local $9) + (i64.const 63) + ) + ) + (block + (set_local $9 + (i64.shl + (get_local $9) + (i64.const 1) + ) + ) + (drop + (block (result i32) + (set_local $8 + (get_local $5) + ) + (set_local $5 + (i32.sub + (get_local $8) + (i32.const 1) + ) + ) + (get_local $8) + ) + ) + (br $continue|1) + ) + ) + ) + ) + (set_local $3 + (i64.shl + (get_local $3) + (i64.add + (i64.sub + (i64.const 0) + (i64.extend_u/i32 + (get_local $5) + ) + ) + (i64.const 1) + ) + ) + ) + ) + (block + (set_local $3 + (i64.and + (get_local $3) + (i64.shr_u + (i64.sub + (i64.const 0) + (i64.const 1) + ) + (i64.const 12) + ) + ) + ) + (set_local $3 + (i64.or + (get_local $3) + (i64.shl + (i64.const 1) + (i64.const 52) + ) + ) + ) + ) + ) + (block $break|2 + (nop) + (loop $continue|2 + (if + (i32.gt_s + (get_local $4) + (get_local $5) + ) + (block + (block + (set_local $9 + (i64.sub + (get_local $2) + (get_local $3) + ) + ) + (if + (i64.eqz + (i64.shr_u + (get_local $9) + (i64.const 63) + ) + ) + (block + (if + (i64.eqz + (get_local $9) + ) + (return + (f64.mul + (f64.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $9) + ) + ) + ) + (set_local $2 + (i64.shl + (get_local $2) + (i64.const 1) + ) + ) + ) + (drop + (block (result i32) + (set_local $8 + (get_local $4) + ) + (set_local $4 + (i32.sub + (get_local $8) + (i32.const 1) + ) + ) + (get_local $8) + ) + ) + (br $continue|2) + ) + ) + ) + ) + (set_local $9 + (i64.sub + (get_local $2) + (get_local $3) + ) + ) + (if + (i64.eqz + (i64.shr_u + (get_local $9) + (i64.const 63) + ) + ) + (block + (if + (i64.eqz + (get_local $9) + ) + (return + (f64.mul + (f64.const 0) + (get_local $0) + ) + ) + ) + (set_local $2 + (get_local $9) + ) + ) + ) + (block $break|3 + (nop) + (loop $continue|3 + (if + (i64.eqz + (i64.shr_u + (get_local $2) + (i64.const 52) + ) + ) + (block + (set_local $2 + (i64.shl + (get_local $2) + (i64.const 1) + ) + ) + (drop + (block (result i32) + (set_local $8 + (get_local $4) + ) + (set_local $4 + (i32.sub + (get_local $8) + (i32.const 1) + ) + ) + (get_local $8) + ) + ) + (br $continue|3) + ) + ) + ) + ) + (if + (i32.gt_s + (get_local $4) + (i32.const 0) + ) + (block + (set_local $2 + (i64.sub + (get_local $2) + (i64.shl + (i64.const 1) + (i64.const 52) + ) + ) + ) + (set_local $2 + (i64.or + (get_local $2) + (i64.shl + (i64.extend_u/i32 + (get_local $4) + ) + (i64.const 52) + ) + ) + ) + ) + (set_local $2 + (i64.shr_u + (get_local $2) + (i64.add + (i64.sub + (i64.const 0) + (i64.extend_u/i32 + (get_local $4) + ) + ) + (i64.const 1) + ) + ) + ) + ) + (set_local $2 + (i64.or + (get_local $2) + (i64.shl + (i64.extend_u/i32 + (get_local $6) + ) + (i64.const 63) + ) + ) + ) + (return + (f64.reinterpret/i64 + (get_local $2) + ) + ) + ) + (func $start (; 1 ;) (type $v) + (local $0 f64) + (if + (i32.eqz + (f64.ne + (tee_local $0 + (call $fmod/fmod + (f64.const 1) + (f64.const nan:0x8000000000000) + ) + ) + (get_local $0) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f64.eq + (call $fmod/fmod + (f64.const 1.5) + (f64.const 1) + ) + (f64.const 0.5) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f64.lt + (f64.sub + (call $fmod/fmod + (f64.const 9.2) + (f64.const 2) + ) + (f64.const 1.2) + ) + (f64.const 2.220446049250313e-16) + ) + ) + (unreachable) + ) + (if + (i32.eqz + (f64.lt + (f64.sub + (call $fmod/fmod + (f64.const 9.2) + (f64.const 3.7) + ) + (f64.const 1.8) + ) + (f64.const 2.220446049250313e-16) + ) + ) + (unreachable) + ) + ) +) +(; +[program.elements] + GLOBAL: NaN + GLOBAL: Infinity + FUNCTION_PROTOTYPE: isNaN + FUNCTION_PROTOTYPE: isFinite + FUNCTION_PROTOTYPE: clz + FUNCTION_PROTOTYPE: ctz + FUNCTION_PROTOTYPE: popcnt + FUNCTION_PROTOTYPE: rotl + FUNCTION_PROTOTYPE: rotr + FUNCTION_PROTOTYPE: abs + FUNCTION_PROTOTYPE: max + FUNCTION_PROTOTYPE: min + FUNCTION_PROTOTYPE: ceil + FUNCTION_PROTOTYPE: floor + FUNCTION_PROTOTYPE: copysign + FUNCTION_PROTOTYPE: nearest + FUNCTION_PROTOTYPE: reinterpret + FUNCTION_PROTOTYPE: sqrt + FUNCTION_PROTOTYPE: trunc + FUNCTION_PROTOTYPE: load + FUNCTION_PROTOTYPE: store + FUNCTION_PROTOTYPE: sizeof + FUNCTION_PROTOTYPE: select + FUNCTION_PROTOTYPE: unreachable + FUNCTION_PROTOTYPE: current_memory + FUNCTION_PROTOTYPE: grow_memory + FUNCTION_PROTOTYPE: parseInt + FUNCTION_PROTOTYPE: parseFloat + FUNCTION_PROTOTYPE: changetype + FUNCTION_PROTOTYPE: assert + FUNCTION_PROTOTYPE: i8 + FUNCTION_PROTOTYPE: i16 + FUNCTION_PROTOTYPE: i32 + FUNCTION_PROTOTYPE: i64 + FUNCTION_PROTOTYPE: u8 + FUNCTION_PROTOTYPE: u16 + FUNCTION_PROTOTYPE: u32 + FUNCTION_PROTOTYPE: u64 + FUNCTION_PROTOTYPE: bool + FUNCTION_PROTOTYPE: f32 + FUNCTION_PROTOTYPE: f64 + FUNCTION_PROTOTYPE: isize + FUNCTION_PROTOTYPE: usize + GLOBAL: HEAP_BASE + FUNCTION_PROTOTYPE: fmod/fmod +[program.exports] + FUNCTION_PROTOTYPE: fmod/fmod +;)