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

Add support for i128 and u128 #4222

Merged
merged 12 commits into from
Nov 11, 2024
6 changes: 6 additions & 0 deletions crates/cli-support/src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ tys! {
U32
I64
U64
I128
U128
F32
F64
BOOLEAN
Expand Down Expand Up @@ -55,6 +57,8 @@ pub enum Descriptor {
U32,
I64,
U64,
I128,
U128,
F32,
F64,
Boolean,
Expand Down Expand Up @@ -132,11 +136,13 @@ impl Descriptor {
I16 => Descriptor::I16,
I32 => Descriptor::I32,
I64 => Descriptor::I64,
I128 => Descriptor::I128,
U8 if clamped => Descriptor::ClampedU8,
U8 => Descriptor::U8,
U16 => Descriptor::U16,
U32 => Descriptor::U32,
U64 => Descriptor::U64,
U128 => Descriptor::U128,
F32 => Descriptor::F32,
F64 => Descriptor::F64,
BOOLEAN => Descriptor::Boolean,
Expand Down
59 changes: 53 additions & 6 deletions crates/cli-support/src/js/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,23 @@ fn instruction(
)
}

fn int128_to_int64x2(val: &str) -> (String, String) {
// we don't need to perform any conversion here, because the JS
// WebAssembly API will automatically convert the bigints to 64 bits
// for us. This even allows us to ignore signedness.
let low = val.to_owned();
let high = format!("{val} >> 64n");
(low, high)
}
fn int64x2_to_int128(low: String, high: String, signed: bool) -> String {
let low = format!("BigInt.asUintN(64, {low})");
if signed {
format!("({low} | ({high} << 64n))")
} else {
format!("({low} | (BigInt.asUintN(64, {high}) << 64n))")
}
}

match instr {
Instruction::ArgGet(n) => {
let arg = js.arg(*n).to_string();
Expand Down Expand Up @@ -712,6 +729,36 @@ fn instruction(
}
}

Instruction::Int128ToWasm { .. } => {
let val = js.pop();
js.assert_bigint(&val);
let (low, high) = int128_to_int64x2(&val);
js.push(low);
js.push(high);
}
Instruction::WasmToInt128 { signed } => {
let high = js.pop();
let low = js.pop();
js.push(int64x2_to_int128(low, high, *signed));
}

Instruction::OptionInt128ToWasm { .. } => {
let val = js.pop();
js.cx.expose_is_like_none();
js.assert_optional_bigint(&val);
let (low, high) = int128_to_int64x2(&val);
js.push(format!("!isLikeNone({val})"));
js.push(format!("isLikeNone({val}) ? 0n : {low}"));
js.push(format!("isLikeNone({val}) ? 0n : {high}"));
}
Instruction::OptionWasmToInt128 { signed } => {
let high = js.pop();
let low = js.pop();
let present = js.pop();
let val = int64x2_to_int128(low, high, *signed);
js.push(format!("{present} === 0 ? undefined : {val}"));
}

Instruction::WasmToStringEnum { name } => {
let index = js.pop();
js.cx.expose_string_enum(name);
Expand Down Expand Up @@ -982,11 +1029,7 @@ fn instruction(
js.push(format!("!isLikeNone({0})", val));
js.push(format!(
"isLikeNone({val}) ? {zero} : {val}",
zero = if *ty == ValType::I64 {
"BigInt(0)"
} else {
"0"
}
zero = if *ty == ValType::I64 { "0n" } else { "0" }
RunDevelopment marked this conversation as resolved.
Show resolved Hide resolved
));
}

Expand Down Expand Up @@ -1500,7 +1543,11 @@ fn adapter2ts(ty: &AdapterType, dst: &mut String, refs: Option<&mut HashSet<TsRe
| AdapterType::F32
| AdapterType::F64
| AdapterType::NonNull => dst.push_str("number"),
AdapterType::I64 | AdapterType::S64 | AdapterType::U64 => dst.push_str("bigint"),
AdapterType::I64
| AdapterType::S64
| AdapterType::U64
| AdapterType::S128
| AdapterType::U128 => dst.push_str("bigint"),
AdapterType::String => dst.push_str("string"),
AdapterType::Externref => dst.push_str("any"),
AdapterType::Bool => dst.push_str("boolean"),
Expand Down
28 changes: 28 additions & 0 deletions crates/cli-support/src/wit/incoming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ impl InstructionBuilder<'_, '_> {
Descriptor::U32 => self.number(AdapterType::U32, WasmVT::I32),
Descriptor::I64 => self.number(AdapterType::S64, WasmVT::I64),
Descriptor::U64 => self.number(AdapterType::U64, WasmVT::I64),
Descriptor::I128 => {
self.instruction(
&[AdapterType::S128],
Instruction::Int128ToWasm { signed: true },
&[AdapterType::I64, AdapterType::I64],
);
}
Descriptor::U128 => {
self.instruction(
&[AdapterType::U128],
Instruction::Int128ToWasm { signed: false },
&[AdapterType::I64, AdapterType::I64],
);
}
Descriptor::F32 => {
self.get(AdapterType::F32);
self.output.push(AdapterType::F32);
Expand Down Expand Up @@ -285,6 +299,20 @@ impl InstructionBuilder<'_, '_> {
Descriptor::F32 => self.in_option_sentinel64_f32(AdapterType::F32),
Descriptor::F64 => self.in_option_native(ValType::F64),
Descriptor::I64 | Descriptor::U64 => self.in_option_native(ValType::I64),
Descriptor::I128 => {
self.instruction(
&[AdapterType::S128.option()],
Instruction::OptionInt128ToWasm { signed: true },
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
);
}
Descriptor::U128 => {
self.instruction(
&[AdapterType::U128.option()],
Instruction::OptionInt128ToWasm { signed: false },
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
);
}
Descriptor::Boolean => {
self.instruction(
&[AdapterType::Bool.option()],
Expand Down
30 changes: 30 additions & 0 deletions crates/cli-support/src/wit/outgoing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ impl InstructionBuilder<'_, '_> {
Descriptor::U32 => self.outgoing_i32(AdapterType::U32),
Descriptor::I64 => self.outgoing_i64(AdapterType::I64),
Descriptor::U64 => self.outgoing_i64(AdapterType::U64),
Descriptor::I128 => {
self.instruction(
&[AdapterType::I64, AdapterType::I64],
Instruction::WasmToInt128 { signed: true },
&[AdapterType::S128],
);
}
Descriptor::U128 => {
self.instruction(
&[AdapterType::I64, AdapterType::I64],
Instruction::WasmToInt128 { signed: false },
&[AdapterType::U128],
);
}
Descriptor::F32 => {
self.get(AdapterType::F32);
self.output.push(AdapterType::F32);
Expand Down Expand Up @@ -267,6 +281,20 @@ impl InstructionBuilder<'_, '_> {
Descriptor::U64 => self.option_native(false, ValType::I64),
Descriptor::F32 => self.out_option_sentinel64(AdapterType::F32),
Descriptor::F64 => self.option_native(true, ValType::F64),
Descriptor::I128 => {
self.instruction(
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
Instruction::OptionWasmToInt128 { signed: true },
&[AdapterType::S128.option()],
);
}
Descriptor::U128 => {
self.instruction(
&[AdapterType::I32, AdapterType::I64, AdapterType::I64],
Instruction::OptionWasmToInt128 { signed: false },
&[AdapterType::U128.option()],
);
}
Descriptor::Boolean => {
self.instruction(
&[AdapterType::I32],
Expand Down Expand Up @@ -360,6 +388,8 @@ impl InstructionBuilder<'_, '_> {
| Descriptor::F64
| Descriptor::I64
| Descriptor::U64
| Descriptor::I128
| Descriptor::U128
| Descriptor::Boolean
| Descriptor::Char
| Descriptor::Enum { .. }
Expand Down
18 changes: 18 additions & 0 deletions crates/cli-support/src/wit/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,12 @@ pub enum AdapterType {
S16,
S32,
S64,
S128,
U8,
U16,
U32,
U64,
U128,
F32,
F64,
String,
Expand Down Expand Up @@ -145,6 +147,22 @@ pub enum Instruction {
output: AdapterType,
},

/// Pops a 128-bit integer and pushes 2 Wasm 64-bit ints.
Int128ToWasm {
signed: bool,
},
/// Pops 2 Wasm 64-bit ints and pushes a 128-bit integer.
WasmToInt128 {
signed: bool,
},

OptionInt128ToWasm {
signed: bool,
},
OptionWasmToInt128 {
signed: bool,
},

/// Pops a Wasm `i32` and pushes the enum variant as a string
WasmToStringEnum {
name: String,
Expand Down
6 changes: 6 additions & 0 deletions crates/cli/tests/reference/int128.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* tslint:disable */
/* eslint-disable */
export function echo_i128(a: bigint): bigint;
export function echo_u128(a: bigint): bigint;
export function echo_option_i128(a?: bigint): bigint | undefined;
export function echo_option_u128(a?: bigint): bigint | undefined;
83 changes: 83 additions & 0 deletions crates/cli/tests/reference/int128.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
let wasm;
export function __wbg_set_wasm(val) {
wasm = val;
}


let cachedDataViewMemory0 = null;

function getDataViewMemory0() {
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
}
return cachedDataViewMemory0;
}
/**
* @param {bigint} a
* @returns {bigint}
*/
export function echo_i128(a) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.echo_i128(retptr, a, a >> 64n);
var r0 = getDataViewMemory0().getBigInt64(retptr + 8 * 0, true);
var r2 = getDataViewMemory0().getBigInt64(retptr + 8 * 1, true);
return (BigInt.asUintN(64, r0) | (r2 << 64n));
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}

/**
* @param {bigint} a
* @returns {bigint}
*/
export function echo_u128(a) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
wasm.echo_u128(retptr, a, a >> 64n);
var r0 = getDataViewMemory0().getBigInt64(retptr + 8 * 0, true);
var r2 = getDataViewMemory0().getBigInt64(retptr + 8 * 1, true);
return (BigInt.asUintN(64, r0) | (BigInt.asUintN(64, r2) << 64n));
} finally {
wasm.__wbindgen_add_to_stack_pointer(16);
}
}

function isLikeNone(x) {
return x === undefined || x === null;
}
/**
* @param {bigint | undefined} [a]
* @returns {bigint | undefined}
*/
export function echo_option_i128(a) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-32);
wasm.echo_option_i128(retptr, !isLikeNone(a), isLikeNone(a) ? 0n : a, isLikeNone(a) ? 0n : a >> 64n);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r2 = getDataViewMemory0().getBigInt64(retptr + 8 * 1, true);
var r4 = getDataViewMemory0().getBigInt64(retptr + 8 * 2, true);
return r0 === 0 ? undefined : (BigInt.asUintN(64, r2) | (r4 << 64n));
} finally {
wasm.__wbindgen_add_to_stack_pointer(32);
}
}

/**
* @param {bigint | undefined} [a]
* @returns {bigint | undefined}
*/
export function echo_option_u128(a) {
try {
const retptr = wasm.__wbindgen_add_to_stack_pointer(-32);
wasm.echo_option_u128(retptr, !isLikeNone(a), isLikeNone(a) ? 0n : a, isLikeNone(a) ? 0n : a >> 64n);
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
var r2 = getDataViewMemory0().getBigInt64(retptr + 8 * 1, true);
var r4 = getDataViewMemory0().getBigInt64(retptr + 8 * 2, true);
return r0 === 0 ? undefined : (BigInt.asUintN(64, r2) | (BigInt.asUintN(64, r4) << 64n));
} finally {
wasm.__wbindgen_add_to_stack_pointer(32);
}
}

19 changes: 19 additions & 0 deletions crates/cli/tests/reference/int128.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn echo_i128(a: i128) -> i128 {
a
}
#[wasm_bindgen]
pub fn echo_u128(a: u128) -> u128 {
a
}

#[wasm_bindgen]
pub fn echo_option_i128(a: Option<i128>) -> Option<i128> {
a
}
#[wasm_bindgen]
pub fn echo_option_u128(a: Option<u128>) -> Option<u128> {
a
}
19 changes: 19 additions & 0 deletions crates/cli/tests/reference/int128.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
(module $reference_test.wasm
(type (;0;) (func (param i32) (result i32)))
(type (;1;) (func (param i32 i32 i64 i64)))
(type (;2;) (func (param i32 i64 i64)))
(func $echo_option_i128 (;0;) (type 1) (param i32 i32 i64 i64))
(func $echo_option_u128 (;1;) (type 1) (param i32 i32 i64 i64))
(func $echo_i128 (;2;) (type 2) (param i32 i64 i64))
(func $echo_u128 (;3;) (type 2) (param i32 i64 i64))
(func $__wbindgen_add_to_stack_pointer (;4;) (type 0) (param i32) (result i32))
(memory (;0;) 17)
(export "memory" (memory 0))
(export "echo_i128" (func $echo_i128))
(export "echo_u128" (func $echo_u128))
(export "echo_option_i128" (func $echo_option_i128))
(export "echo_option_u128" (func $echo_option_u128))
(export "__wbindgen_add_to_stack_pointer" (func $__wbindgen_add_to_stack_pointer))
(@custom "target_features" (after code) "\02+\0fmutable-globals+\08sign-ext")
)

Loading