Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable BigInt for binaryen.js #7167

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@ full changeset diff at the end of each section.
Current Trunk
-------------

- Binaryen.js now builds with BigInt support. The i64.const and f64.const_bits
functions now take BigInt parameters rather than pairs of numbers.

v121
----

17 changes: 3 additions & 14 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -326,20 +326,9 @@ else()
endif()

if(EMSCRIPTEN)
# Note: to debug with DWARF you will usually want to enable BIGINT support, as
# that helps avoid running Binaryen on the wasm after link. Binaryen's DWARF
# rewriting has known limitations, so avoiding it during link is recommended
# where possible (like local debugging).
#
# Note that this is debug info for Binaryen itself, that is, when you are
# debugging Binaryen source code. This flag has no impact on what Binaryen
# does when run on wasm files.
option(ENABLE_BIGINT "Enable wasm BigInt support" OFF)
if(ENABLE_BIGINT)
add_link_flag("-sWASM_BIGINT")
else()
add_link_flag("-sWASM_BIGINT=0")
endif()
# This is now on by default in Emscripten, but set it explicitly to continue
# building correctly on older Emscriptens.
add_link_flag("-sWASM_BIGINT")

if("${CMAKE_BUILD_TYPE}" MATCHES "Release")
# Extra check that cmake has set -O3 in its release flags.
8 changes: 4 additions & 4 deletions src/js/binaryen.js-post.js
Original file line number Diff line number Diff line change
@@ -1049,10 +1049,10 @@ function wrapModule(module, self = {}) {
'store32'(offset, align, ptr, value, name) {
return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['i64'], strToStack(name));
},
'const'(x, y) {
'const'(x) {
return preserveStack(() => {
const tempLiteral = stackAlloc(sizeOfLiteral);
Module['_BinaryenLiteralInt64'](tempLiteral, x, y);
Module['_BinaryenLiteralInt64'](tempLiteral, BigInt(x));
Copy link
Member

@kripken kripken Dec 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about making this backwards compatible? We could support (i32, undefined), (i32, i32), and (BigInt, undefined) with this code:

        // Convert to a BigInt. We support (x, undefined) where x is either a
        // BigInt or not, and also (x, y) where neither is a BigInt and y is the
        // higher bits.
        if (typeof x !== 'bigint') {
          x = BigInt(x || 0) | (BigInt(y || 0) << 32n);
        }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A small code size cost seems reasonable to me here, personally. And it is nice to write binaryen.i64.const(42) and not need to remember 42n each time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

binaryen.i64.const(42) already works because of the explicit BigInt(x) conversion here. Except for compatibility, I don't think there's much benefit to supporting the two-argument version. How much do we value compatibility here? Would we have a deprecation plan, or would we support the two-argument version indefinitely?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think compatibility makes sense: there are users that are creating i64s with two arguments, and I don't see a strong reason to break them, given the burden on supporting the old format is minimal.

return Module['_BinaryenConst'](module, tempLiteral);
});
},
@@ -1438,10 +1438,10 @@ function wrapModule(module, self = {}) {
return Module['_BinaryenConst'](module, tempLiteral);
});
},
'const_bits'(x, y) {
'const_bits'(x) {
return preserveStack(() => {
const tempLiteral = stackAlloc(sizeOfLiteral);
Module['_BinaryenLiteralFloat64Bits'](tempLiteral, x, y);
Module['_BinaryenLiteralFloat64Bits'](tempLiteral, BigInt(x));
return Module['_BinaryenConst'](module, tempLiteral);
});
},
62 changes: 31 additions & 31 deletions test/binaryen.js/kitchen-sink.js
Original file line number Diff line number Diff line change
@@ -24,8 +24,8 @@ function makeFloat32(x) {
return module.f32.const(x);
}

function makeInt64(l, h) {
return module.i64.const(l, h);
function makeInt64(x) {
return module.i64.const(x);
}

function makeFloat64(x) {
@@ -214,7 +214,7 @@ function test_core() {
constF32 = module.f32.const(3.14),
constF64 = module.f64.const(2.1828),
constF32Bits = module.f32.const_bits(0xffff1234),
constF64Bits = module.f64.const_bits(0x5678abcd, 0xffff1234);
constF64Bits = module.f64.const_bits(0xffff12345678ABCDn)

var iIfF = binaryen.createType([binaryen.i32, binaryen.i64, binaryen.f32, binaryen.f64])

@@ -229,7 +229,7 @@ function test_core() {
var valueList = [
// Unary
module.i32.clz(module.i32.const(-10)),
module.i64.ctz(module.i64.const(-22, -1)),
module.i64.ctz(module.i64.const(295147905179352825834n)),
module.i32.popcnt(module.i32.const(-10)),
module.f32.neg(module.f32.const(-33.612)),
module.f64.abs(module.f64.const(-9005.841)),
@@ -241,7 +241,7 @@ function test_core() {
module.i32.eqz(module.i32.const(-10)),
module.i64.extend_s(module.i32.const(-10)),
module.i64.extend_u(module.i32.const(-10)),
module.i32.wrap(module.i64.const(-22, -1)),
module.i32.wrap(module.i64.const(295147905179352825834n)),
module.i32.trunc_s.f32(module.f32.const(-33.612)),
module.i64.trunc_s.f32(module.f32.const(-33.612)),
module.i32.trunc_u.f32(module.f32.const(-33.612)),
@@ -264,18 +264,18 @@ function test_core() {
module.f64.convert_s.i32(module.i32.const(-10)),
module.f32.convert_u.i32(module.i32.const(-10)),
module.f64.convert_u.i32(module.i32.const(-10)),
module.f32.convert_s.i64(module.i64.const(-22, -1)),
module.f64.convert_s.i64(module.i64.const(-22, -1)),
module.f32.convert_u.i64(module.i64.const(-22, -1)),
module.f64.convert_u.i64(module.i64.const(-22, -1)),
module.f32.convert_s.i64(module.i64.const(295147905179352825834n)),
module.f64.convert_s.i64(module.i64.const(295147905179352825834n)),
module.f32.convert_u.i64(module.i64.const(295147905179352825834n)),
module.f64.convert_u.i64(module.i64.const(295147905179352825834n)),
module.f64.promote(module.f32.const(-33.612)),
module.f32.demote(module.f64.const(-9005.841)),
module.f32.reinterpret(module.i32.const(-10)),
module.f64.reinterpret(module.i64.const(-22, -1)),
module.f64.reinterpret(module.i64.const(295147905179352825834n)),
module.i8x16.splat(module.i32.const(42)),
module.i16x8.splat(module.i32.const(42)),
module.i32x4.splat(module.i32.const(42)),
module.i64x2.splat(module.i64.const(123, 456)),
module.i64x2.splat(module.i64.const(1958505087099n)),
module.f32x4.splat(module.f32.const(42.0)),
module.f64x2.splat(module.f64.const(42.0)),
module.v128.not(module.v128.const(v128_bytes)),
@@ -333,31 +333,31 @@ function test_core() {
module.i32.add(module.i32.const(-10), module.i32.const(-11)),
module.f64.sub(module.f64.const(-9005.841), module.f64.const(-9007.333)),
module.i32.div_s(module.i32.const(-10), module.i32.const(-11)),
module.i64.div_u(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.rem_s(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.div_u(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i64.rem_s(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i32.rem_u(module.i32.const(-10), module.i32.const(-11)),
module.i32.and(module.i32.const(-10), module.i32.const(-11)),
module.i64.or(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.or(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i32.xor(module.i32.const(-10), module.i32.const(-11)),
module.i64.shl(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.shr_u(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.shl(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i64.shr_u(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i32.shr_s(module.i32.const(-10), module.i32.const(-11)),
module.i32.rotl(module.i32.const(-10), module.i32.const(-11)),
module.i64.rotr(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.rotr(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.f32.div(module.f32.const(-33.612), module.f32.const(-62.5)),
module.f64.copysign(module.f64.const(-9005.841), module.f64.const(-9007.333)),
module.f32.min(module.f32.const(-33.612), module.f32.const(-62.5)),
module.f64.max(module.f64.const(-9005.841), module.f64.const(-9007.333)),
module.i32.eq(module.i32.const(-10), module.i32.const(-11)),
module.f32.ne(module.f32.const(-33.612), module.f32.const(-62.5)),
module.i32.lt_s(module.i32.const(-10), module.i32.const(-11)),
module.i64.lt_u(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.le_s(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.lt_u(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i64.le_s(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i32.le_u(module.i32.const(-10), module.i32.const(-11)),
module.i64.gt_s(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.gt_s(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.i32.gt_u(module.i32.const(-10), module.i32.const(-11)),
module.i32.ge_s(module.i32.const(-10), module.i32.const(-11)),
module.i64.ge_u(module.i64.const(-22, 0), module.i64.const(-23, 0)),
module.i64.ge_u(module.i64.const(4294967274n), module.i64.const(4294967273n)),
module.f32.lt(module.f32.const(-33.612), module.f32.const(-62.5)),
module.f64.le(module.f64.const(-9005.841), module.f64.const(-9007.333)),
module.f64.gt(module.f64.const(-9005.841), module.f64.const(-9007.333)),
@@ -502,7 +502,7 @@ function test_core() {
module.i16x8.replace_lane(module.v128.const(v128_bytes), 1, module.i32.const(42)),
module.i8x16.replace_lane(module.v128.const(v128_bytes), 1, module.i32.const(42)),
module.i32x4.replace_lane(module.v128.const(v128_bytes), 1, module.i32.const(42)),
module.i64x2.replace_lane(module.v128.const(v128_bytes), 1, module.i64.const(42, 43)),
module.i64x2.replace_lane(module.v128.const(v128_bytes), 1, module.i64.const(184683593770n)),
module.f32x4.replace_lane(module.v128.const(v128_bytes), 1, module.f32.const(42)),
module.f64x2.replace_lane(module.v128.const(v128_bytes), 1, module.f64.const(42)),
// SIMD shift
@@ -569,15 +569,15 @@ function test_core() {
module.switch([ "the-value" ], "the-value", temp8, temp9),
module.switch([ "the-nothing" ], "the-nothing", makeInt32(2)),
module.i32.eqz( // check the output type of the call node
module.call("kitchen()sinker", [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], binaryen.i32)
module.call("kitchen()sinker", [ makeInt32(13), makeInt64(37), makeFloat32(1.3), makeFloat64(3.7) ], binaryen.i32)
),
module.i32.eqz( // check the output type of the call node
module.i32.trunc_s.f32(
module.call("an-imported", [ makeInt32(13), makeFloat64(3.7) ], binaryen.f32)
)
),
module.i32.eqz( // check the output type of the call node
module.call_indirect("t0", makeInt32(2449), [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32)
module.call_indirect("t0", makeInt32(2449), [ makeInt32(13), makeInt64(37), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32)
),
module.drop(module.local.get(0, binaryen.i32)),
module.local.set(0, makeInt32(101)),
@@ -591,8 +591,8 @@ function test_core() {
module.select(temp10, temp11, temp12),
module.return(makeInt32(1337)),
// Tail Call
module.return_call("kitchen()sinker", [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], binaryen.i32),
module.return_call_indirect("t0", makeInt32(2449), [ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32),
module.return_call("kitchen()sinker", [ makeInt32(13), makeInt64(37), makeFloat32(1.3), makeFloat64(3.7) ], binaryen.i32),
module.return_call_indirect("t0", makeInt32(2449), [ makeInt32(13), makeInt64(37), makeFloat32(1.3), makeFloat64(3.7) ], iIfF, binaryen.i32),

// Reference types
module.ref.is_null(module.ref.null(binaryen.externref)),
@@ -636,11 +636,11 @@ function test_core() {

// Tuples
module.tuple.make(
[ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ]
[ makeInt32(13), makeInt64(37), makeFloat32(1.3), makeFloat64(3.7) ]
),
module.tuple.extract(
module.tuple.make(
[ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ]
[ makeInt32(13), makeInt64(37), makeFloat32(1.3), makeFloat64(3.7) ]
), 2
),

@@ -695,11 +695,11 @@ function test_core() {
}

console.log("getExpressionInfo(i32.const)=" + JSON.stringify(binaryen.getExpressionInfo(module.i32.const(5))));
console.log("getExpressionInfo(i64.const)=" + JSON.stringify(binaryen.getExpressionInfo(module.i64.const(6, 7))));
console.log("getExpressionInfo(i64.const)=" + JSON.stringify(binaryen.getExpressionInfo(module.i64.const(30064771078n))));
console.log("getExpressionInfo(f32.const)=" + JSON.stringify(binaryen.getExpressionInfo(module.f32.const(8.5))));
console.log("getExpressionInfo(f64.const)=" + JSON.stringify(binaryen.getExpressionInfo(module.f64.const(9.5))));
var elements = binaryen.getExpressionInfo(
module.tuple.make([ makeInt32(13), makeInt64(37, 0), makeFloat32(1.3), makeFloat64(3.7) ])
module.tuple.make([ makeInt32(13), makeInt64(37), makeFloat32(1.3), makeFloat64(3.7) ])
).operands;
for (var i = 0; i < elements.length; i++) {
console.log("getExpressionInfo(tuple[" + i + "])=" + JSON.stringify(binaryen.getExpressionInfo(elements[i])));
@@ -1018,7 +1018,7 @@ function test_nonvalid() {
module = new binaryen.Module();

var func = module.addFunction("func", binaryen.none, binaryen.none, [ binaryen.i32 ],
module.local.set(0, makeInt64(1234, 0)) // wrong type!
module.local.set(0, makeInt64(1234)) // wrong type!
);

console.log(module.emitText());
4 changes: 2 additions & 2 deletions test/binaryen.js/validation_errors.js
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
)
])
);
mod.addExport("test", func);
mod.addExport("test", "test");
console.log(mod.validate())
})();

@@ -20,6 +20,6 @@
)
])
);
mod.addFunctionExport("test", "test", func);
mod.addFunctionExport("test", "test");
console.log(mod.validate())
})();
Loading
Oops, something went wrong.