From 37d2a42041331f10e78b319ebc19f874cb639d6d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 9 Jun 2023 13:34:00 -0700 Subject: [PATCH 1/7] fix --- src/parseTools.js | 11 ++++++++--- test/core/js_library_i64_params.c | 6 ++++++ test/core/js_library_i64_params.js | 3 +++ test/core/js_library_i64_params.out | 5 +++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/parseTools.js b/src/parseTools.js index 1f562108d8e9c..44fa66e7edd95 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -885,11 +885,16 @@ function receiveI64ParamAsI53Unchecked(name) { return `var ${name} = convertI32PairToI53(${name}_low, ${name}_high);`; } -function sendI64Argument(low, high) { +// Given the name of a variable containing an unsigned 53-bit integer in a JS +// Number or BigInt, send it as a 64-bit argument in a call to wasm. This will +// legalize the argument if necessary (i.e., split it into two i32 arguments, +// if legalization is used). +function sendU53ToI64Param(name) { + // TODO: In ASSERTIONS mode add range checks here. if (WASM_BIGINT) { - return 'BigInt(low) | (BigInt(high) << 32n)'; + return `BigInt(${name})`; } - return low + ', ' + high; + return `(${name} | 0), (${name} / (2**32))`; } // Any function called from wasm64 may have bigint args, this function takes diff --git a/test/core/js_library_i64_params.c b/test/core/js_library_i64_params.c index 4f0cc33ad98a5..52c4ca78724d1 100644 --- a/test/core/js_library_i64_params.c +++ b/test/core/js_library_i64_params.c @@ -1,4 +1,5 @@ #include +#include #include #define MAX_SAFE_INTEGER (1ll<<53) @@ -8,6 +9,11 @@ int64_t jscall(int64_t arg); +EMSCRIPTEN_KEEPALIVE +void called_from_js(uint64_t arg) { + printf("called_from_js with: %lld\n", arg); +} + void check_ok(int64_t val) { printf("checking: %lld\n", val); int64_t rtn = jscall(val); diff --git a/test/core/js_library_i64_params.js b/test/core/js_library_i64_params.js index 28a00cf1174df..bafe1ff03d521 100644 --- a/test/core/js_library_i64_params.js +++ b/test/core/js_library_i64_params.js @@ -4,6 +4,9 @@ TestLibrary = { jscall: function({{{ defineI64Param('foo') }}}) { {{{ receiveI64ParamAsI53('foo', `(err('overflow'), ${makeReturn64('42')})`) }}} err('js:got: ' + foo); + + _called_from_js({{{ sendU53ToI64Param("foo") }}}); + if (foo < 0) var rtn = Math.ceil(foo / 2); else diff --git a/test/core/js_library_i64_params.out b/test/core/js_library_i64_params.out index 8f1d9b62dcc6b..893cc6da220ac 100644 --- a/test/core/js_library_i64_params.out +++ b/test/core/js_library_i64_params.out @@ -1,25 +1,30 @@ checking: 42 js:got: 42 +called_from_js with: 42 js:returning: 21 got: 21 expected: 21 checking: 4503599627370496 js:got: 4503599627370496 +called_from_js with: 4503599627370496 js:returning: 2251799813685248 got: 2251799813685248 expected: 2251799813685248 checking: -4503599627370496 js:got: -4503599627370496 +called_from_js with: -4503599627370496 js:returning: -2251799813685248 got: -2251799813685248 expected: -2251799813685248 checking: 9007199254740992 js:got: 9007199254740992 +called_from_js with: 9007199254740992 js:returning: 4503599627370496 got: 4503599627370496 expected: 4503599627370496 checking: -9007199254740992 js:got: -9007199254740992 +called_from_js with: -9007199254740992 js:returning: -4503599627370496 got: -4503599627370496 expected: -4503599627370496 From 561f1556eab38e6e8b6391ccbd70ade65139e07b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Jun 2023 13:13:11 -0700 Subject: [PATCH 2/7] signed --- src/library_int53.js | 17 ++++++++++++++++- src/parseTools.js | 10 ++++++---- test/core/js_library_i64_params.js | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/library_int53.js b/src/library_int53.js index 5443caf1f3b5f..7173ceee79d62 100644 --- a/src/library_int53.js +++ b/src/library_int53.js @@ -123,6 +123,17 @@ mergeInto(LibraryManager.library, { return (lo >>> 0) + (hi >>> 0) * 4294967296; }, + // Given a signed i53 number, emit the low 32 bits. This is useful for + // calling into wasm when legalization is needed, and then instead of just + // doing f(num) you would do f(sendI53BitsLow(num), sendI53BitsHigh(num)). + $sendI53BitsLow: function(num) { + return num | 0; + }, + $sendI53BitsHigh: function(num) { + num = Math.floor(num / 4294967296); + return num | 0; + }, + #if WASM_BIGINT $MAX_INT53: '{{{ Math.pow(2, 53) }}}', $MIN_INT53: '-{{{ Math.pow(2, 53) }}}', @@ -140,5 +151,9 @@ mergeInto(LibraryManager.library, { #if WASM_BIGINT global.i53ConversionDeps = ['$bigintToI53Checked']; #else -global.i53ConversionDeps = ['$convertI32PairToI53Checked']; +global.i53ConversionDeps = [ + '$convertI32PairToI53Checked', + '$sendI53BitsLow', + '$sendI53BitsHigh', +]; #endif diff --git a/src/parseTools.js b/src/parseTools.js index d3bc58b5e9fc3..8d150e619dda8 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -886,20 +886,22 @@ function receiveI64ParamAsI53(name, onError) { } function receiveI64ParamAsI53Unchecked(name) { - if (WASM_BIGINT) return `${name} = Number(${name});`; + if (WASM_BIGINT) { + return `${name} = Number(${name});`; + } return `var ${name} = convertI32PairToI53(${name}_low, ${name}_high);`; } -// Given the name of a variable containing an unsigned 53-bit integer in a JS +// Given the name of a variable containing a signed 53-bit integer in a JS // Number or BigInt, send it as a 64-bit argument in a call to wasm. This will // legalize the argument if necessary (i.e., split it into two i32 arguments, // if legalization is used). -function sendU53ToI64Param(name) { +function sendI53ToI64Param(name) { // TODO: In ASSERTIONS mode add range checks here. if (WASM_BIGINT) { return `BigInt(${name})`; } - return `(${name} | 0), (${name} / (2**32))`; + return `sendI53BitsLow(${name}), sendI53BitsHigh(${name})`; } // Any function called from wasm64 may have bigint args, this function takes diff --git a/test/core/js_library_i64_params.js b/test/core/js_library_i64_params.js index bafe1ff03d521..5840d06dd04ee 100644 --- a/test/core/js_library_i64_params.js +++ b/test/core/js_library_i64_params.js @@ -5,7 +5,7 @@ TestLibrary = { {{{ receiveI64ParamAsI53('foo', `(err('overflow'), ${makeReturn64('42')})`) }}} err('js:got: ' + foo); - _called_from_js({{{ sendU53ToI64Param("foo") }}}); + _called_from_js({{{ sendI53ToI64Param("foo") }}}); if (foo < 0) var rtn = Math.ceil(foo / 2); From 9f67e9150f6ca3521505fe4ebde4f019be1ab9ff Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Jun 2023 13:14:22 -0700 Subject: [PATCH 3/7] test -42 as well --- test/core/js_library_i64_params.c | 1 + test/core/js_library_i64_params.out | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/test/core/js_library_i64_params.c b/test/core/js_library_i64_params.c index 52c4ca78724d1..134309d93b56f 100644 --- a/test/core/js_library_i64_params.c +++ b/test/core/js_library_i64_params.c @@ -32,6 +32,7 @@ void check_invalid(int64_t val) { int main() { check_ok(42); + check_ok(-42); check_ok(MAX_SAFE_INTEGER/2); check_ok(MIN_SAFE_INTEGER/2); check_ok(MAX_SAFE_INTEGER); diff --git a/test/core/js_library_i64_params.out b/test/core/js_library_i64_params.out index 893cc6da220ac..0867645450a01 100644 --- a/test/core/js_library_i64_params.out +++ b/test/core/js_library_i64_params.out @@ -4,6 +4,12 @@ called_from_js with: 42 js:returning: 21 got: 21 expected: 21 +checking: -42 +js:got: -42 +called_from_js with: -42 +js:returning: -21 +got: -21 +expected: -21 checking: 4503599627370496 js:got: 4503599627370496 called_from_js with: 4503599627370496 From 0da560984ad91f8bdb282ec86c0c7146693f465a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Jun 2023 13:18:15 -0700 Subject: [PATCH 4/7] TODOs --- src/library_int53.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/library_int53.js b/src/library_int53.js index 7173ceee79d62..6f1aca0c6839b 100644 --- a/src/library_int53.js +++ b/src/library_int53.js @@ -127,9 +127,11 @@ mergeInto(LibraryManager.library, { // calling into wasm when legalization is needed, and then instead of just // doing f(num) you would do f(sendI53BitsLow(num), sendI53BitsHigh(num)). $sendI53BitsLow: function(num) { + // TODO: assert on num being in the valid i53 range return num | 0; }, $sendI53BitsHigh: function(num) { + // TODO: assert on num being in the valid i53 range num = Math.floor(num / 4294967296); return num | 0; }, From c979f287b5718653724906351f020a5b039b81e4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Jun 2023 14:24:24 -0700 Subject: [PATCH 5/7] test --- test/core/js_library_i64_params.c | 3 +++ test/core/js_library_i64_params.out | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/test/core/js_library_i64_params.c b/test/core/js_library_i64_params.c index 134309d93b56f..764fc20b0e490 100644 --- a/test/core/js_library_i64_params.c +++ b/test/core/js_library_i64_params.c @@ -31,6 +31,9 @@ void check_invalid(int64_t val) { } int main() { + check_ok(0); + check_ok(1); + check_ok(-1); check_ok(42); check_ok(-42); check_ok(MAX_SAFE_INTEGER/2); diff --git a/test/core/js_library_i64_params.out b/test/core/js_library_i64_params.out index 0867645450a01..26db9827949b3 100644 --- a/test/core/js_library_i64_params.out +++ b/test/core/js_library_i64_params.out @@ -1,3 +1,21 @@ +checking: 0 +js:got: 0 +called_from_js with: 0 +js:returning: 0 +got: 0 +expected: 0 +checking: 1 +js:got: 1 +called_from_js with: 1 +js:returning: 0 +got: 0 +expected: 0 +checking: -1 +js:got: -1 +called_from_js with: -1 +js:returning: 0 +got: 0 +expected: 0 checking: 42 js:got: 42 called_from_js with: 42 From abac05f4ad408ff235459cd989649649d30f94a2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Jun 2023 14:29:07 -0700 Subject: [PATCH 6/7] undo and use splitI64 --- src/library_int53.js | 19 +------------------ src/parseTools.js | 12 ------------ test/core/js_library_i64_params.js | 2 +- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/src/library_int53.js b/src/library_int53.js index 6f1aca0c6839b..5443caf1f3b5f 100644 --- a/src/library_int53.js +++ b/src/library_int53.js @@ -123,19 +123,6 @@ mergeInto(LibraryManager.library, { return (lo >>> 0) + (hi >>> 0) * 4294967296; }, - // Given a signed i53 number, emit the low 32 bits. This is useful for - // calling into wasm when legalization is needed, and then instead of just - // doing f(num) you would do f(sendI53BitsLow(num), sendI53BitsHigh(num)). - $sendI53BitsLow: function(num) { - // TODO: assert on num being in the valid i53 range - return num | 0; - }, - $sendI53BitsHigh: function(num) { - // TODO: assert on num being in the valid i53 range - num = Math.floor(num / 4294967296); - return num | 0; - }, - #if WASM_BIGINT $MAX_INT53: '{{{ Math.pow(2, 53) }}}', $MIN_INT53: '-{{{ Math.pow(2, 53) }}}', @@ -153,9 +140,5 @@ mergeInto(LibraryManager.library, { #if WASM_BIGINT global.i53ConversionDeps = ['$bigintToI53Checked']; #else -global.i53ConversionDeps = [ - '$convertI32PairToI53Checked', - '$sendI53BitsLow', - '$sendI53BitsHigh', -]; +global.i53ConversionDeps = ['$convertI32PairToI53Checked']; #endif diff --git a/src/parseTools.js b/src/parseTools.js index 8d150e619dda8..04ff16394ebdb 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -892,18 +892,6 @@ function receiveI64ParamAsI53Unchecked(name) { return `var ${name} = convertI32PairToI53(${name}_low, ${name}_high);`; } -// Given the name of a variable containing a signed 53-bit integer in a JS -// Number or BigInt, send it as a 64-bit argument in a call to wasm. This will -// legalize the argument if necessary (i.e., split it into two i32 arguments, -// if legalization is used). -function sendI53ToI64Param(name) { - // TODO: In ASSERTIONS mode add range checks here. - if (WASM_BIGINT) { - return `BigInt(${name})`; - } - return `sendI53BitsLow(${name}), sendI53BitsHigh(${name})`; -} - // Any function called from wasm64 may have bigint args, this function takes // a list of variable names to convert to number. function from64(x) { diff --git a/test/core/js_library_i64_params.js b/test/core/js_library_i64_params.js index 5840d06dd04ee..6c9f886c81e78 100644 --- a/test/core/js_library_i64_params.js +++ b/test/core/js_library_i64_params.js @@ -5,7 +5,7 @@ TestLibrary = { {{{ receiveI64ParamAsI53('foo', `(err('overflow'), ${makeReturn64('42')})`) }}} err('js:got: ' + foo); - _called_from_js({{{ sendI53ToI64Param("foo") }}}); + _called_from_js({{{ splitI64("foo") }}}); if (foo < 0) var rtn = Math.ceil(foo / 2); From d45e7ce13703aa6e014711b9d2c5355e0614ef0c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 12 Jun 2023 15:18:09 -0700 Subject: [PATCH 7/7] fix.bigint --- src/parseTools.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/parseTools.js b/src/parseTools.js index 04ff16394ebdb..d6c47b6f2edbd 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -213,6 +213,12 @@ function makeInlineCalculation(expression, value, tempVar) { // value, represented by a low and high i32 pair. // Will suffer from rounding and truncation. function splitI64(value) { + if (WASM_BIGINT) { + // Nothing to do: just make sure it is a BigInt (as it must be of that + // type, to be sent into wasm). + return `BigInt(${value})`; + } + // general idea: // // $1$0 = ~~$d >>> 0; @@ -229,7 +235,6 @@ function splitI64(value) { // For negatives, we need to ensure a -1 if the value is overall negative, // even if not significant negative component - assert(!WASM_BIGINT, 'splitI64 should not be used when WASM_BIGINT is enabled'); const low = value + '>>>0'; const high = makeInlineCalculation( asmCoercion('Math.abs(VALUE)', 'double') + ' >= ' + asmEnsureFloat('1', 'double') + ' ? ' +