Move fmod/fmodf to builtins and bind them to the '%' operator

This commit is contained in:
dcodeIO 2018-03-24 18:39:20 +01:00
parent 721d77012b
commit c80bf35747
21 changed files with 4378 additions and 4209 deletions

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -821,7 +821,7 @@ export class Compiler extends DiagnosticEmitter {
/** Compiles a readily resolved function instance. */
compileFunction(instance: Function): bool {
if (instance.is(CommonFlags.COMPILED)) return true;
assert(!instance.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || instance.simpleName == "abort");
assert(!instance.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || instance.internalName == "abort");
instance.set(CommonFlags.COMPILED);
// check that modifiers are matching but still compile as-is
@ -3227,14 +3227,42 @@ export class Compiler extends DiagnosticEmitter {
expr = module.createBinary(BinaryOp.RemU64, leftExpr, rightExpr);
break;
}
case TypeKind.F32:
case TypeKind.F32: {
let fmodPrototype = this.program.elementsLookup.get("fmodf");
if (!fmodPrototype) {
this.error(
DiagnosticCode.Cannot_find_name_0,
expression.range, "fmod"
);
expr = module.createUnreachable();
break;
}
assert(fmodPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
let fmodInstance = (<FunctionPrototype>fmodPrototype).resolve();
if (!(fmodInstance && this.compileFunction(fmodInstance))) {
expr = module.createUnreachable();
} else {
expr = this.makeCallDirect(fmodInstance, [ leftExpr, rightExpr ]);
}
break;
}
case TypeKind.F64: {
// TODO: internal fmod, possibly simply imported from JS
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
);
expr = module.createUnreachable();
let fmodPrototype = this.program.elementsLookup.get("fmod");
if (!fmodPrototype) {
this.error(
DiagnosticCode.Cannot_find_name_0,
expression.range, "fmod"
);
expr = module.createUnreachable();
break;
}
assert(fmodPrototype.kind == ElementKind.FUNCTION_PROTOTYPE);
let fmodInstance = (<FunctionPrototype>fmodPrototype).resolve();
if (!(fmodInstance && this.compileFunction(fmodInstance))) {
expr = module.createUnreachable();
} else {
expr = this.makeCallDirect(fmodInstance, [ leftExpr, rightExpr ]);
}
break;
}
default: {

View File

@ -2514,7 +2514,7 @@ export class Function extends Element {
this.signature = signature;
this.memberOf = memberOf;
this.flags = prototype.flags;
if (!(prototype.is(CommonFlags.BUILTIN) || prototype.is(CommonFlags.DECLARE))) {
if (!(prototype.is(CommonFlags.AMBIENT | CommonFlags.BUILTIN) || prototype.is(CommonFlags.DECLARE))) {
let localIndex = 0;
if (memberOf && memberOf.kind == ElementKind.CLASS) {
assert(this.is(CommonFlags.INSTANCE));

4
std/assembly.d.ts vendored
View File

@ -249,6 +249,10 @@ declare function parseI32(str: string, radix?: i32): i32;
declare function parseI64(str: string, radix?: i32): i64;
/** Parses a string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
// Standard library

View File

@ -172,3 +172,123 @@ export namespace f64 {
export declare const HEAP_BASE: usize;
export declare function start(): void;
export function fmod(x: f64, y: f64): f64 {
// based on musl's implementation of fmod
var ux = reinterpret<u64>(x);
var uy = reinterpret<u64>(y);
var ex = <i32>(ux >> 52 & 0x7ff);
var ey = <i32>(uy >> 52 & 0x7ff);
var sx = <i32>(ux >> 63);
if (uy << 1 == 0 || isNaN<f64>(y) || ex == 0x7ff) {
return (x * y) / (x * y);
}
if (ux << 1 <= uy << 1) {
if (ux << 1 == uy << 1) return 0 * x;
return x;
}
// normalize x and y
var i: u64;
if (!ex) {
for (i = ux << 12; !(i >> 63); i <<= 1) --ex;
ux <<= -ex + 1;
} else {
ux &= <u64>-1 >> 12;
ux |= 1 << 52;
}
if (!ey) {
for (i = uy << 12; !(i >> 63); i <<= 1) --ey;
uy <<= -ey + 1;
} else {
uy &= <u64>-1 >> 12;
uy |= 1 << 52;
}
// x mod y
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); ux <<= 1) --ex;
// scale result
if (ex > 0) {
ux -= 1 << 52;
ux |= <u64>ex << 52;
} else {
ux >>= -ex + 1;
}
ux |= <u64>sx << 63;
return reinterpret<f64>(ux);
}
export function fmodf(x: f32, y: f32): f32 {
// based on musl's implementation of fmodf
var ux = reinterpret<u32>(x);
var uy = reinterpret<u32>(y);
var ex = <i32>(ux >> 23 & 0xff);
var ey = <i32>(uy >> 23 & 0xff);
var sx = ux & 0x80000000;
if (uy << 1 == 0 || isNaN<f32>(y) || ex == 0xff) {
return (x * y) / (x * y);
}
if (ux << 1 <= uy << 1) {
if (ux << 1 == uy << 1) return 0 * x;
return x;
}
// normalize x and y
var i: u32;
if (!ex) {
for (i = ux << 9; !(i >> 31); i <<= 1) --ex;
ux <<= -ex + 1;
} else {
ux &= <u32>-1 >> 9;
ux |= 1 << 23;
}
if (!ey) {
for (i = uy << 9; !(i >> 31); i <<= 1) --ey;
uy <<= -ey + 1;
} else {
uy &= <u32>-1 >> 9;
uy |= 1 << 23;
}
// x mod y
for (; ex > ey; --ex) {
i = ux - uy;
if (!(i >> 31)) {
if (!i) return 0 * x;
ux = i;
}
ux <<= 1;
}
i = ux - uy;
if (!(i >> 31)) {
if (!i) return 0 * x;
ux = i;
}
for (; !(ux >> 23); ux <<= 1) --ex;
// scale result
if (ex > 0) {
ux -= 1 << 23;
ux |= <u32>ex << 23;
} else {
ux >>= -ex + 1;
}
ux |= sx;
return reinterpret<f32>(ux);
}

95
std/portable.d.ts vendored
View File

@ -193,6 +193,10 @@ declare function parseInt(str: string, radix?: i32): f64;
declare function parseI32(str: string, radix?: i32): i32;
/** Parses a floating point string to a 64-bit float. */
declare function parseFloat(str: string): f64;
/** Returns the 64-bit floating-point remainder of `x/y`. */
declare function fmod(x: f64, y: f64): f64;
/** Returns the 32-bit floating-point remainder of `x/y`. */
declare function fmodf(x: f32, y: f32): f32;
// Portable standard library
// Everything marked @deprecated is a temporary filler. Do not use.
@ -310,52 +314,55 @@ interface Iterable<T> {
interface Iterator<T> {}
declare namespace Math {
export const E: f64;
export const LN2: f64;
export const LN10: f64;
export const LOG2E: f64;
export const LOG10E: f64;
export const PI: f64;
export const SQRT1_2: f64;
export const SQRT2: f64;
export function abs(x: f64): f64;
export function acos(x: f64): f64;
export function acosh(x: f64): f64;
export function asin(x: f64): f64;
export function asinh(x: f64): f64;
export function atan(x: f64): f64;
export function atan2(y: f64, x: f64): f64;
export function atanh(x: f64): f64;
export function cbrt(x: f64): f64;
export function ceil(x: f64): f64;
export function clz32(x: f64): i32;
export function cos(x: f64): f64;
export function cosh(x: f64): f64;
export function exp(x: f64): f64;
export function expm1(x: f64): f64;
export function floor(x: f64): f64;
export function fround(x: f64): f32;
export function hypot(value1: f64, value2: f64): f64; // TODO: see std/math
export function imul(a: f64, b: f64): i32;
export function log(x: f64): f64;
export function log10(x: f64): f64;
export function log1p(x: f64): f64;
export function log2(x: f64): f64;
export function max(value1: f64, value2: f64): f64; // TODO: see std/math
export function min(value1: f64, value2: f64): f64; // TODO: see std/math
export function pow(base: f64, exponent: f64): f64;
export function random(): f64;
export function round(x: f64): f64;
export function sign(x: f64): f64;
export function sin(x: f64): f64;
export function sinh(x: f64): f64;
export function sqrt(x: f64): f64;
export function tan(x: f64): f64;
export function tanh(x: f64): f64;
export function trunc(x: f64): f64;
interface IMath {
readonly E: f64;
readonly LN2: f64;
readonly LN10: f64;
readonly LOG2E: f64;
readonly LOG10E: f64;
readonly PI: f64;
readonly SQRT1_2: f64;
readonly SQRT2: f64;
abs(x: f64): f64;
acos(x: f64): f64;
acosh(x: f64): f64;
asin(x: f64): f64;
asinh(x: f64): f64;
atan(x: f64): f64;
atan2(y: f64, x: f64): f64;
atanh(x: f64): f64;
cbrt(x: f64): f64;
ceil(x: f64): f64;
clz32(x: f64): i32;
cos(x: f64): f64;
cosh(x: f64): f64;
exp(x: f64): f64;
expm1(x: f64): f64;
floor(x: f64): f64;
fround(x: f64): f32;
hypot(value1: f64, value2: f64): f64; // TODO: see std/math
imul(a: f64, b: f64): i32;
log(x: f64): f64;
log10(x: f64): f64;
log1p(x: f64): f64;
log2(x: f64): f64;
max(value1: f64, value2: f64): f64; // TODO: see std/math
min(value1: f64, value2: f64): f64; // TODO: see std/math
pow(base: f64, exponent: f64): f64;
random(): f64;
round(x: f64): f64;
sign(x: f64): f64;
sin(x: f64): f64;
sinh(x: f64): f64;
sqrt(x: f64): f64;
tan(x: f64): f64;
tanh(x: f64): f64;
trunc(x: f64): f64;
}
declare const Math: IMath;
declare const JSMath: IMath;
declare namespace console {
/** @deprecated */
function log(message: string): void;

View File

@ -169,4 +169,13 @@ globalScope["isString"] = function isString(arg) {
globalScope["isArray"] = Array.isArray;
require("./portable/memory");
globalScope["fmod"] = function fmod(x, y) {
return x % y;
};
globalScope["fmodf"] = function fmodf(x, y) {
return Math.fround(x % y);
};
require("./portable/math")(globalScope);
require("./portable/memory")(globalScope);

3
std/portable/math.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = globalScope => {
globalScope["JSMath"] = Math;
};

View File

@ -1,38 +1,38 @@
var globalScope = typeof window !== "undefined" && window || typeof global !== "undefined" && global || self;
module.exports = globalScope => {
var HEAP = new Uint8Array(0);
var HEAP_OFFSET = 0;
var HEAP = new Uint8Array(0);
var HEAP_OFFSET = 0;
globalScope["allocate_memory"] = function allocate_memory(size) {
if (!(size >>>= 0))
return 0;
if (HEAP_OFFSET + size > HEAP.length) {
var oldHeap = HEAP;
HEAP = new Uint8Array(Math.max(65536, HEAP.length + size, HEAP.length * 2));
HEAP.set(oldHeap);
}
var ptr = HEAP_OFFSET;
if ((HEAP_OFFSET += size) & 7)
HEAP_OFFSET = (HEAP_OFFSET | 7) + 1;
return ptr;
};
globalScope["allocate_memory"] = function allocate_memory(size) {
if (!(size >>>= 0))
return 0;
if (HEAP_OFFSET + size > HEAP.length) {
var oldHeap = HEAP;
HEAP = new Uint8Array(Math.max(65536, HEAP.length + size, HEAP.length * 2));
HEAP.set(oldHeap);
}
var ptr = HEAP_OFFSET;
if ((HEAP_OFFSET += size) & 7)
HEAP_OFFSET = (HEAP_OFFSET | 7) + 1;
return ptr;
};
globalScope["free_memory"] = function free_memory(ptr) {
// TODO
};
globalScope["move_memory"] = function move_memory(dest, src, n) {
HEAP.copyWithin(dest, src, src + n);
};
globalScope["store"] = function store(ptr, val, off) {
if (typeof off === "number")
ptr += off;
HEAP[ptr] = val;
};
globalScope["load"] = function load(ptr) {
if (typeof off === "number")
ptr += off;
return HEAP[ptr];
globalScope["free_memory"] = function free_memory(ptr) {
// TODO
};
globalScope["move_memory"] = function move_memory(dest, src, n) {
HEAP.copyWithin(dest, src, src + n);
};
globalScope["store"] = function store(ptr, val, off) {
if (typeof off === "number")
ptr += off;
HEAP[ptr] = val;
};
globalScope["load"] = function load(ptr) {
if (typeof off === "number")
ptr += off;
return HEAP[ptr];
};
};

View File

@ -1,6 +1,8 @@
(module
(type $iiiiv (func (param i32 i32 i32 i32)))
(type $v (func))
(type $FFF (func (param f64 f64) (result f64)))
(type $fff (func (param f32 f32) (result f32)))
(import "env" "abort" (func $abort (param i32 i32 i32 i32)))
(global $builtins/b (mut i32) (i32.const 0))
(global $builtins/i (mut i32) (i32.const 0))
@ -19,7 +21,731 @@
(func $builtins/test (; 1 ;) (type $v)
(nop)
)
(func $start (; 2 ;) (type $v)
(func $fmod (; 2 ;) (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 f64)
(block $folding-inner0
(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)
)
)
)
(if
(i32.and
(if (result i32)
(tee_local $7
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
)
(i64.const 0)
)
)
(get_local $7)
(f64.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
)
(i32.const 1)
)
)
(get_local $7)
(i32.eq
(get_local $3)
(i32.const 2047)
)
)
(i32.const 1)
)
(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
(br_if $folding-inner0
(i64.eq
(i64.shl
(get_local $2)
(i64.const 1)
)
(i64.shl
(get_local $5)
(i64.const 1)
)
)
)
(return
(get_local $0)
)
)
)
(set_local $7
(i32.wrap/i64
(i64.shr_u
(get_local $2)
(i64.const 63)
)
)
)
(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 $3
(i32.sub
(get_local $3)
(i32.const 1)
)
)
(set_local $4
(i64.shl
(get_local $4)
(i64.const 1)
)
)
(br $continue|0)
)
)
)
(i64.shl
(get_local $2)
(i64.extend_u/i32
(i32.sub
(i32.const 1)
(get_local $3)
)
)
)
)
)
)
(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 $6
(i32.sub
(get_local $6)
(i32.const 1)
)
)
(set_local $4
(i64.shl
(get_local $4)
(i64.const 1)
)
)
(br $continue|1)
)
)
)
(i64.shl
(get_local $5)
(i64.extend_u/i32
(i32.sub
(i32.const 1)
(get_local $6)
)
)
)
)
)
)
(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
(br_if $folding-inner0
(i64.eqz
(get_local $4)
)
)
(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
(br_if $folding-inner0
(i64.eqz
(get_local $4)
)
)
(set_local $2
(get_local $4)
)
)
)
(loop $continue|3
(if
(i64.eqz
(i64.shr_u
(get_local $2)
(i64.const 52)
)
)
(block
(set_local $3
(i32.sub
(get_local $3)
(i32.const 1)
)
)
(set_local $2
(i64.shl
(get_local $2)
(i64.const 1)
)
)
(br $continue|3)
)
)
)
(return
(f64.reinterpret/i64
(i64.or
(tee_local $2
(select
(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.extend_u/i32
(i32.sub
(i32.const 1)
(get_local $3)
)
)
)
(i32.gt_s
(get_local $3)
(i32.const 0)
)
)
)
(i64.shl
(i64.extend_u/i32
(get_local $7)
)
(i64.const 63)
)
)
)
)
)
(f64.mul
(f64.const 0)
(get_local $0)
)
)
(func $fmodf (; 3 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
(local $7 f32)
(local $8 i32)
(block $folding-inner0
(set_local $4
(i32.and
(i32.shr_u
(tee_local $2
(i32.reinterpret/f32
(get_local $0)
)
)
(i32.const 23)
)
(i32.const 255)
)
)
(set_local $6
(i32.and
(i32.shr_u
(tee_local $5
(i32.reinterpret/f32
(get_local $1)
)
)
(i32.const 23)
)
(i32.const 255)
)
)
(if
(i32.and
(if (result i32)
(tee_local $3
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
)
)
)
(get_local $3)
(f32.ne
(tee_local $7
(get_local $1)
)
(get_local $7)
)
)
(i32.const 1)
)
)
(get_local $3)
(i32.eq
(get_local $4)
(i32.const 255)
)
)
(i32.const 1)
)
(return
(f32.div
(f32.mul
(get_local $0)
(get_local $1)
)
(f32.mul
(get_local $0)
(get_local $1)
)
)
)
)
(if
(i32.le_u
(i32.shl
(get_local $2)
(i32.const 1)
)
(i32.shl
(get_local $5)
(i32.const 1)
)
)
(block
(br_if $folding-inner0
(i32.eq
(i32.shl
(get_local $2)
(i32.const 1)
)
(i32.shl
(get_local $5)
(i32.const 1)
)
)
)
(return
(get_local $0)
)
)
)
(set_local $8
(i32.and
(get_local $2)
(i32.const -2147483648)
)
)
(set_local $2
(if (result i32)
(get_local $4)
(i32.or
(i32.and
(get_local $2)
(i32.const 8388607)
)
(i32.const 8388608)
)
(block (result i32)
(set_local $3
(i32.shl
(get_local $2)
(i32.const 9)
)
)
(loop $continue|0
(if
(i32.eqz
(i32.shr_u
(get_local $3)
(i32.const 31)
)
)
(block
(set_local $4
(i32.sub
(get_local $4)
(i32.const 1)
)
)
(set_local $3
(i32.shl
(get_local $3)
(i32.const 1)
)
)
(br $continue|0)
)
)
)
(i32.shl
(get_local $2)
(i32.sub
(i32.const 1)
(get_local $4)
)
)
)
)
)
(set_local $5
(if (result i32)
(get_local $6)
(i32.or
(i32.and
(get_local $5)
(i32.const 8388607)
)
(i32.const 8388608)
)
(block (result i32)
(set_local $3
(i32.shl
(get_local $5)
(i32.const 9)
)
)
(loop $continue|1
(if
(i32.eqz
(i32.shr_u
(get_local $3)
(i32.const 31)
)
)
(block
(set_local $6
(i32.sub
(get_local $6)
(i32.const 1)
)
)
(set_local $3
(i32.shl
(get_local $3)
(i32.const 1)
)
)
(br $continue|1)
)
)
)
(i32.shl
(get_local $5)
(i32.sub
(i32.const 1)
(get_local $6)
)
)
)
)
)
(loop $continue|2
(if
(i32.gt_s
(get_local $4)
(get_local $6)
)
(block
(if
(i32.eqz
(i32.shr_u
(tee_local $3
(i32.sub
(get_local $2)
(get_local $5)
)
)
(i32.const 31)
)
)
(block
(br_if $folding-inner0
(i32.eqz
(get_local $3)
)
)
(set_local $2
(get_local $3)
)
)
)
(set_local $2
(i32.shl
(get_local $2)
(i32.const 1)
)
)
(set_local $4
(i32.sub
(get_local $4)
(i32.const 1)
)
)
(br $continue|2)
)
)
)
(if
(i32.eqz
(i32.shr_u
(tee_local $3
(i32.sub
(get_local $2)
(get_local $5)
)
)
(i32.const 31)
)
)
(block
(br_if $folding-inner0
(i32.eqz
(get_local $3)
)
)
(set_local $2
(get_local $3)
)
)
)
(loop $continue|3
(if
(i32.eqz
(i32.shr_u
(get_local $2)
(i32.const 23)
)
)
(block
(set_local $4
(i32.sub
(get_local $4)
(i32.const 1)
)
)
(set_local $2
(i32.shl
(get_local $2)
(i32.const 1)
)
)
(br $continue|3)
)
)
)
(return
(f32.reinterpret/i32
(i32.or
(tee_local $2
(select
(i32.or
(i32.sub
(get_local $2)
(i32.const 8388608)
)
(i32.shl
(get_local $4)
(i32.const 23)
)
)
(i32.shr_u
(get_local $2)
(i32.sub
(i32.const 1)
(get_local $4)
)
)
(i32.gt_s
(get_local $4)
(i32.const 0)
)
)
)
(get_local $8)
)
)
)
)
(f32.mul
(f32.const 0)
(get_local $0)
)
)
(func $start (; 4 ;) (type $v)
(local $0 f32)
(local $1 f64)
(local $2 i32)
@ -840,5 +1566,209 @@
(unreachable)
)
)
(if
(f64.eq
(tee_local $1
(call $fmod
(f64.const 1)
(f64.const nan:0x8000000000000)
)
)
(get_local $1)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 295)
(i32.const 0)
)
(unreachable)
)
)
(if
(f64.ne
(call $fmod
(f64.const 1.5)
(f64.const 1)
)
(f64.const 0.5)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 296)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(f64.lt
(f64.sub
(call $fmod
(f64.const 9.2)
(f64.const 2)
)
(f64.const 1.2)
)
(f64.const 2.220446049250313e-16)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 297)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(f64.lt
(f64.sub
(call $fmod
(f64.const 9.2)
(f64.const 3.7)
)
(f64.const 1.8)
)
(f64.const 2.220446049250313e-16)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 298)
(i32.const 0)
)
(unreachable)
)
)
(if
(f32.eq
(tee_local $0
(call $fmodf
(f32.const 1)
(f32.const nan:0x400000)
)
)
(get_local $0)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 300)
(i32.const 0)
)
(unreachable)
)
)
(if
(f32.ne
(call $fmodf
(f32.const 1.5)
(f32.const 1)
)
(f32.const 0.5)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 301)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(f32.lt
(f32.sub
(call $fmodf
(f32.const 9.199999809265137)
(f32.const 2)
)
(f32.const 1.2000000476837158)
)
(f32.const 1.1920928955078125e-07)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 302)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(f32.lt
(f32.sub
(call $fmodf
(f32.const 9.199999809265137)
(f32.const 3.700000047683716)
)
(f32.const 1.7999999523162842)
)
(f32.const 1.1920928955078125e-07)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 303)
(i32.const 0)
)
(unreachable)
)
)
(if
(f64.ne
(call $fmod
(f64.const 1.5)
(f64.const 1)
)
(f64.const 0.5)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 305)
(i32.const 0)
)
(unreachable)
)
)
(if
(f32.ne
(call $fmodf
(f32.const 1.5)
(f32.const 1)
)
(f32.const 0.5)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 306)
(i32.const 0)
)
(unreachable)
)
)
)
)

View File

@ -292,6 +292,19 @@ assert(f64.MIN_SAFE_INTEGER == -9007199254740991);
assert(f64.MAX_SAFE_INTEGER == 9007199254740991);
assert(f64.EPSILON == 2.2204460492503131e-16);
assert(isNaN<f64>(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)
assert(isNaN<f32>(fmodf(1, NaN)));
assert(fmodf(1.5, 1.0) == 0.5);
assert(fmodf(9.2, 2.0) - 1.2 < f32.EPSILON);
assert(fmodf(9.2, 3.7) - 1.8 < f32.EPSILON);
assert(1.5 % 1.0 == 0.5); // should implicitly call fmod
assert(<f32>1.5 % 1.0 == 0.5); // should implicitly call fmodf
import {
isNaN as isItNaN
} from "builtins";

File diff suppressed because it is too large Load Diff

View File

@ -1,909 +0,0 @@
(module
(type $FFF (func (param f64 f64) (result f64)))
(type $iiiiv (func (param i32 i32 i32 i32)))
(type $fff (func (param f32 f32) (result f32)))
(type $v (func))
(import "env" "abort" (func $abort (param i32 i32 i32 i32)))
(memory $0 1)
(data (i32.const 4) "\07\00\00\00f\00m\00o\00d\00.\00t\00s")
(export "fmod" (func $fmod/fmod))
(export "fmodf" (func $fmod/fmodf))
(export "memory" (memory $0))
(start $start)
(func $fmod/fmod (; 1 ;) (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 f64)
(block $folding-inner0
(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)
)
)
)
(if
(i32.and
(if (result i32)
(tee_local $7
(i32.and
(if (result i32)
(tee_local $7
(i64.eq
(i64.shl
(get_local $5)
(i64.const 1)
)
(i64.const 0)
)
)
(get_local $7)
(f64.ne
(tee_local $8
(get_local $1)
)
(get_local $8)
)
)
(i32.const 1)
)
)
(get_local $7)
(i32.eq
(get_local $3)
(i32.const 2047)
)
)
(i32.const 1)
)
(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
(br_if $folding-inner0
(i64.eq
(i64.shl
(get_local $2)
(i64.const 1)
)
(i64.shl
(get_local $5)
(i64.const 1)
)
)
)
(return
(get_local $0)
)
)
)
(set_local $7
(i32.wrap/i64
(i64.shr_u
(get_local $2)
(i64.const 63)
)
)
)
(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 $3
(i32.sub
(get_local $3)
(i32.const 1)
)
)
(set_local $4
(i64.shl
(get_local $4)
(i64.const 1)
)
)
(br $continue|0)
)
)
)
(i64.shl
(get_local $2)
(i64.extend_u/i32
(i32.sub
(i32.const 1)
(get_local $3)
)
)
)
)
)
)
(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 $6
(i32.sub
(get_local $6)
(i32.const 1)
)
)
(set_local $4
(i64.shl
(get_local $4)
(i64.const 1)
)
)
(br $continue|1)
)
)
)
(i64.shl
(get_local $5)
(i64.extend_u/i32
(i32.sub
(i32.const 1)
(get_local $6)
)
)
)
)
)
)
(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
(br_if $folding-inner0
(i64.eqz
(get_local $4)
)
)
(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
(br_if $folding-inner0
(i64.eqz
(get_local $4)
)
)
(set_local $2
(get_local $4)
)
)
)
(loop $continue|3
(if
(i64.eqz
(i64.shr_u
(get_local $2)
(i64.const 52)
)
)
(block
(set_local $3
(i32.sub
(get_local $3)
(i32.const 1)
)
)
(set_local $2
(i64.shl
(get_local $2)
(i64.const 1)
)
)
(br $continue|3)
)
)
)
(return
(f64.reinterpret/i64
(i64.or
(tee_local $2
(select
(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.extend_u/i32
(i32.sub
(i32.const 1)
(get_local $3)
)
)
)
(i32.gt_s
(get_local $3)
(i32.const 0)
)
)
)
(i64.shl
(i64.extend_u/i32
(get_local $7)
)
(i64.const 63)
)
)
)
)
)
(f64.mul
(f64.const 0)
(get_local $0)
)
)
(func $fmod/fmodf (; 2 ;) (type $fff) (param $0 f32) (param $1 f32) (result f32)
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
(local $7 f32)
(local $8 i32)
(block $folding-inner0
(set_local $4
(i32.and
(i32.shr_u
(tee_local $2
(i32.reinterpret/f32
(get_local $0)
)
)
(i32.const 23)
)
(i32.const 255)
)
)
(set_local $6
(i32.and
(i32.shr_u
(tee_local $5
(i32.reinterpret/f32
(get_local $1)
)
)
(i32.const 23)
)
(i32.const 255)
)
)
(if
(i32.and
(if (result i32)
(tee_local $3
(i32.and
(if (result i32)
(tee_local $3
(i32.eqz
(i32.shl
(get_local $5)
(i32.const 1)
)
)
)
(get_local $3)
(f32.ne
(tee_local $7
(get_local $1)
)
(get_local $7)
)
)
(i32.const 1)
)
)
(get_local $3)
(i32.eq
(get_local $4)
(i32.const 255)
)
)
(i32.const 1)
)
(return
(f32.div
(f32.mul
(get_local $0)
(get_local $1)
)
(f32.mul
(get_local $0)
(get_local $1)
)
)
)
)
(if
(i32.le_u
(i32.shl
(get_local $2)
(i32.const 1)
)
(i32.shl
(get_local $5)
(i32.const 1)
)
)
(block
(br_if $folding-inner0
(i32.eq
(i32.shl
(get_local $2)
(i32.const 1)
)
(i32.shl
(get_local $5)
(i32.const 1)
)
)
)
(return
(get_local $0)
)
)
)
(set_local $8
(i32.and
(get_local $2)
(i32.const -2147483648)
)
)
(set_local $2
(if (result i32)
(get_local $4)
(i32.or
(i32.and
(get_local $2)
(i32.const 8388607)
)
(i32.const 8388608)
)
(block (result i32)
(set_local $3
(i32.shl
(get_local $2)
(i32.const 9)
)
)
(loop $continue|0
(if
(i32.eqz
(i32.shr_u
(get_local $3)
(i32.const 31)
)
)
(block
(set_local $4
(i32.sub
(get_local $4)
(i32.const 1)
)
)
(set_local $3
(i32.shl
(get_local $3)
(i32.const 1)
)
)
(br $continue|0)
)
)
)
(i32.shl
(get_local $2)
(i32.sub
(i32.const 1)
(get_local $4)
)
)
)
)
)
(set_local $5
(if (result i32)
(get_local $6)
(i32.or
(i32.and
(get_local $5)
(i32.const 8388607)
)
(i32.const 8388608)
)
(block (result i32)
(set_local $3
(i32.shl
(get_local $5)
(i32.const 9)
)
)
(loop $continue|1
(if
(i32.eqz
(i32.shr_u
(get_local $3)
(i32.const 31)
)
)
(block
(set_local $6
(i32.sub
(get_local $6)
(i32.const 1)
)
)
(set_local $3
(i32.shl
(get_local $3)
(i32.const 1)
)
)
(br $continue|1)
)
)
)
(i32.shl
(get_local $5)
(i32.sub
(i32.const 1)
(get_local $6)
)
)
)
)
)
(loop $continue|2
(if
(i32.gt_s
(get_local $4)
(get_local $6)
)
(block
(if
(i32.eqz
(i32.shr_u
(tee_local $3
(i32.sub
(get_local $2)
(get_local $5)
)
)
(i32.const 31)
)
)
(block
(br_if $folding-inner0
(i32.eqz
(get_local $3)
)
)
(set_local $2
(get_local $3)
)
)
)
(set_local $2
(i32.shl
(get_local $2)
(i32.const 1)
)
)
(set_local $4
(i32.sub
(get_local $4)
(i32.const 1)
)
)
(br $continue|2)
)
)
)
(if
(i32.eqz
(i32.shr_u
(tee_local $3
(i32.sub
(get_local $2)
(get_local $5)
)
)
(i32.const 31)
)
)
(block
(br_if $folding-inner0
(i32.eqz
(get_local $3)
)
)
(set_local $2
(get_local $3)
)
)
)
(loop $continue|3
(if
(i32.eqz
(i32.shr_u
(get_local $2)
(i32.const 23)
)
)
(block
(set_local $4
(i32.sub
(get_local $4)
(i32.const 1)
)
)
(set_local $2
(i32.shl
(get_local $2)
(i32.const 1)
)
)
(br $continue|3)
)
)
)
(return
(f32.reinterpret/i32
(i32.or
(tee_local $2
(select
(i32.or
(i32.sub
(get_local $2)
(i32.const 8388608)
)
(i32.shl
(get_local $4)
(i32.const 23)
)
)
(i32.shr_u
(get_local $2)
(i32.sub
(i32.const 1)
(get_local $4)
)
)
(i32.gt_s
(get_local $4)
(i32.const 0)
)
)
)
(get_local $8)
)
)
)
)
(f32.mul
(f32.const 0)
(get_local $0)
)
)
(func $start (; 3 ;) (type $v)
(local $0 f64)
(local $1 f32)
(if
(f64.eq
(tee_local $0
(call $fmod/fmod
(f64.const 1)
(f64.const nan:0x8000000000000)
)
)
(get_local $0)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 65)
(i32.const 0)
)
(unreachable)
)
)
(if
(f64.ne
(call $fmod/fmod
(f64.const 1.5)
(f64.const 1)
)
(f64.const 0.5)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 66)
(i32.const 0)
)
(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)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 67)
(i32.const 0)
)
(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)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 68)
(i32.const 0)
)
(unreachable)
)
)
(if
(f32.eq
(tee_local $1
(call $fmod/fmodf
(f32.const 1)
(f32.const nan:0x400000)
)
)
(get_local $1)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 134)
(i32.const 0)
)
(unreachable)
)
)
(if
(f32.ne
(call $fmod/fmodf
(f32.const 1.5)
(f32.const 1)
)
(f32.const 0.5)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 135)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(f32.lt
(f32.sub
(call $fmod/fmodf
(f32.const 9.199999809265137)
(f32.const 2)
)
(f32.const 1.2000000476837158)
)
(f32.const 1.1920928955078125e-07)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 136)
(i32.const 0)
)
(unreachable)
)
)
(if
(i32.eqz
(f32.lt
(f32.sub
(call $fmod/fmodf
(f32.const 9.199999809265137)
(f32.const 3.700000047683716)
)
(f32.const 1.7999999523162842)
)
(f32.const 1.1920928955078125e-07)
)
)
(block
(call $abort
(i32.const 0)
(i32.const 4)
(i32.const 137)
(i32.const 0)
)
(unreachable)
)
)
)
)

View File

@ -1,137 +0,0 @@
export function fmod(x: f64, y: f64): f64 {
// the following is based on musl's implementation of fmod
var ux = reinterpret<u64>(x);
var uy = reinterpret<u64>(y);
var ex = <i32>(ux >> 52 & 0x7ff);
var ey = <i32>(uy >> 52 & 0x7ff);
var sx = <i32>(ux >> 63);
if (uy << 1 == 0 || isNaN<f64>(y) || ex == 0x7ff)
return (x * y) / (x * y);
if (ux << 1 <= uy << 1) {
if (ux << 1 == uy << 1)
return 0 * x;
return x;
}
// normalize x and y
if (!ex) {
for (var i = ux << 12; !(i >> 63); i <<= 1)
--ex;
ux <<= -ex + 1;
} else {
ux &= <u64>-1 >> 12;
ux |= 1 << 52;
}
if (!ey) {
for (i = uy << 12; !(i >> 63); i <<= 1)
--ey;
uy <<= -ey + 1;
} else {
uy &= <u64>-1 >> 12;
uy |= 1 << 52;
}
// x mod y
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); ux <<= 1)
--ex;
// scale result
if (ex > 0) {
ux -= 1 << 52;
ux |= <u64>ex << 52;
} else {
ux >>= -ex + 1;
}
ux |= <u64>sx << 63;
return reinterpret<f64>(ux);
}
assert(isNaN<f64>(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)
export function fmodf(x: f32, y: f32): f32 {
// the following is based on musl's implementation of fmodf
var ux = reinterpret<u32>(x);
var uy = reinterpret<u32>(y);
var ex = <i32>(ux >> 23 & 0xff);
var ey = <i32>(uy >> 23 & 0xff);
var sx = ux & 0x80000000;
if (uy << 1 == 0 || isNaN<f32>(y) || ex == 0xff)
return (x * y) / (x * y);
if (ux << 1 <= uy << 1) {
if (ux << 1 == uy << 1)
return 0 * x;
return x;
}
// normalize x and y
if (!ex) {
for (var i = ux << 9; !(i >> 31); i <<= 1)
--ex;
ux <<= -ex + 1;
} else {
ux &= <u32>-1 >> 9;
ux |= 1 << 23;
}
if (!ey) {
for (i = uy << 9; !(i >> 31); i <<= 1)
--ey;
uy <<= -ey + 1;
} else {
uy &= <u32>-1 >> 9;
uy |= 1 << 23;
}
// x mod y
for (; ex > ey; --ex) {
i = ux - uy;
if (!(i >> 31)) {
if (!i)
return 0 * x;
ux = i;
}
ux <<= 1;
}
i = ux - uy;
if (!(i >> 31)) {
if (!i)
return 0 * x;
ux = i;
}
for (; !(ux >> 23); ux <<= 1)
--ex;
// scale result
if (ex > 0) {
ux -= 1 << 23;
ux |= <u32>ex << 23;
} else {
ux >>= -ex + 1;
}
ux |= sx;
return reinterpret<f32>(ux);
}
assert(isNaN<f32>(fmodf(1, NaN)));
assert(fmodf(1.5, 1.0) == 0.5);
assert(fmodf(9.2, 2.0) - 1.2 < f32.EPSILON);
assert(fmodf(9.2, 3.7) - 1.8 < f32.EPSILON);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -85,7 +85,6 @@ export function anExportedFunction(): void { }
// be implemented already. Here are a few more sophisitcated examples of code that'll most likely
// make it into the standard library eventually:
import "./memcpy"; // until replaced by the proposed `move_memory` intrinsic (sic.)
import "./fmod"; // for floating point modulus support, e.g., `1.5 % 1.0`
// Speaking of classes: Some preliminary work has already been done, so while we can't properly
// instantiate them yet, we can point them at some raw memory

File diff suppressed because it is too large Load Diff