Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/compiler/src/encoding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,11 @@ export const op = {
* Numeric Instructions
* https://webassembly.github.io/spec/core/binary/instructions.html#numeric-instructions
*/
i32_const: (i: number) => [0x41, ...signedLEB128(i)],
// i64_const: 0x42,
// TODO: Add bounds checking
i32_const: (n: number) => [0x41, ...signedLEB128(n)],
i64_const: (n: number) => [0x42, ...signedLEB128(n)],
// f32_const: 0x43,
f64_const: (i: number) => [0x44, ...encodef64(i)],
f64_const: (n: number) => [0x44, ...encodef64(n)],

i32_eqz: 0x45,
// i32_eq: 0x46,
Expand All @@ -114,6 +115,7 @@ export const op = {
// i32_ge_u: 0x4f,

// [0x50...0x5a] i64
i64_ne: 0x52,
// [0x5b...0x60] f32

f64_eq: 0x61,
Expand Down Expand Up @@ -164,6 +166,9 @@ export const op = {
i64_trunc_s_f64: 0xb0,
f64_convert_i64_s: 0xb9,
f64_convert_i32_s: 0xb7,

i64_trunc_f64_s: 0xb0,
i64_trunc_f64_u: 0xb1,
};

// https://webassembly.github.io/spec/core/binary/instructions.html#binary-blocktype
Expand Down
51 changes: 41 additions & 10 deletions packages/compiler/src/wasmFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,39 @@ import { op, VAL_TYPE, IS_NOT_ZEROISH, EPSILON, BLOCK } from "./encoding.js";
import { FunctionDefinition } from "./types.js";
import { BUFFER_SIZE } from "./constants.js";

// Minimum value that x87 would treat as out of range for conversion
// from float to int.
const MIN_OUT_OF_RANGE = op.f64_const(Math.pow(2, 31));
// Arbitrary value that x87 uses when trying to convert an out of range, or NaN,
// float to an int.

const INDETERMINATE = op.f64_const(Math.pow(2, 31));

/**
* Reimplements the details of how x87 handles out of range values in the `fist`
* instruction.
*
* Expects a local argument offset and leaves an i64 value on the stack. We use
* i64 since teh indeterminate value is 2^31, which is just out of the range of
* i32.
*
* See https://godbolt.org/z/rjf769E3n for some examples
*/
function localGetAsBoundedPositiveInt(argOffset: number): number[] {
return [
...op.local_get(argOffset),
...MIN_OUT_OF_RANGE,
op.f64_lt,
...op.if(BLOCK.f64),
...op.local_get(argOffset),
op.f64_abs,
op.else,
...INDETERMINATE,
op.end,
op.i64_trunc_f64_s,
];
}

export const localFuncMap: { [functionName: string]: FunctionDefinition } = {
sqr: {
args: [VAL_TYPE.f64],
Expand Down Expand Up @@ -53,20 +86,18 @@ export const localFuncMap: { [functionName: string]: FunctionDefinition } = {
mod: {
args: [VAL_TYPE.f64, VAL_TYPE.f64],
returns: [VAL_TYPE.f64],
localVariables: [VAL_TYPE.i32],
// TODO: Simplify all this type coercion
localVariables: [VAL_TYPE.i64],
// Tuned to match https://github.com/WACUP/vis_milk2/blob/de9625a89e724afe23ed273b96b8e48496095b6c/ns-eel2/asm-nseel-x86-gcc.c#L507-L531
binary: [
...op.local_get(1),
op.i32_trunc_f64_s,
...localGetAsBoundedPositiveInt(1),
...op.local_tee(2),
...op.i32_const(0),
op.i32_ne,
...op.i64_const(0),
op.i64_ne,
...op.if(BLOCK.f64),
...op.local_get(0),
op.i32_trunc_f64_s,
...localGetAsBoundedPositiveInt(0),
...op.local_get(2),
op.i32_rem_s,
op.f64_convert_i32_s,
op.i64_rem_s,
op.f64_convert_i64_s,
op.else,
...op.f64_const(0),
op.end,
Expand Down
14 changes: 11 additions & 3 deletions packages/compiler/tools/testCases.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
const testCases: [string, string, number][] = [
// https://github.com/captbaritone/eel-wasm/issues/61
["Mod with NaN in dividend", "g = asin(2) % 2;", 0], // The indeterminate is even
["Mod with large even value in dividend", "g = 56991359368.52679 % 2;", 0], // The indeterminate is even
["Mod with large odd value in dividend", "g = 56991359367.52679 % 2;", 0], // The indeterminate is even
["Mod with large even value in divisor", "g = 300 % 56991359368.52679;", 300],
["Mod with large odd value in divisor", "g = 300 % 56991359367.52679;", 300],
["Mod with NaN in divisor", "g = 300 % asin(2);", 300],
["Expressions", "g = ((6- -7)+ 3);", 16],
["Number", "g = 5;", 5],
["Number with decimal", "g = 5.5;", 5.5],
Expand Down Expand Up @@ -39,18 +46,19 @@ const testCases: [string, string, number][] = [
["Cos", "g = cos(10);", Math.cos(10)],
["Tan", "g = tan(10);", Math.tan(10)],
["Asin", "g = asin(0.5);", Math.asin(0.5)],
["Asin", "g = asin(2);", NaN],
["Acos", "g = acos(0.5);", Math.acos(0.5)],
["Atan", "g = atan(0.5);", Math.atan(0.5)],
["Atan2", "g = atan2(1, 1);", Math.atan2(1, 1)],
["Assign to globals", "g = 10;", 10],
["Read globals", "g = x;", 10],
["Multiple statements", "g = 10; g = 20;", 20],
["Multiple statements expression", "(g = 10; g = 20;);", 20],
["Multiple statements expression implcit return", "g = (0; 20 + 5;);", 25],
["Multiple statements expression implicit return", "g = (0; 20 + 5;);", 25],
["if", "g = if(0, 20, 10);", 10],
["if", "g = if(0, 20, 10);", 10],
["if does short-circit (consiquent)", "if(0, (g = 10;), 10);", 0],
["if does short-circit (alternate)", "if(1, (10), (g = 10;));", 0],
["if does short-circuit (consequent)", "if(0, (g = 10;), 10);", 0],
["if does short-circuit (alternate)", "if(1, (10), (g = 10;));", 0],
["above (true)", "g = above(10, 4);", 1],
["above (false)", "g = above(4, 10);", 0],
["below (true)", "g = below(4, 10);", 1],
Expand Down