Skip to content

Commit 894ecba

Browse files
authored
Improve __clz128 and __ctz128 (#93)
1 parent 20da778 commit 894ecba

File tree

1 file changed

+30
-5
lines changed

1 file changed

+30
-5
lines changed

assembly/globals.ts

+30-5
Original file line numberDiff line numberDiff line change
@@ -229,18 +229,43 @@ export function __floatuntfdi(value: f64): u64 {
229229
}
230230
}
231231

232+
233+
/**
234+
* Count leading zeros in a 128-bit integer [hi:lo], returning i32 in [0..128].
235+
* If both hi and lo are 0, returns 128.
236+
*
237+
* hi is signed in i128, but we interpret it as unsigned here.
238+
*/
232239
// @ts-ignore: decorator
233240
@global @inline
234-
export function __clz128(lo: u64, hi: u64): i32 {
235-
var mask: u64 = <i64>(hi ^ (hi - 1)) >> 63;
236-
return <i32>clz((hi & ~mask) | (lo & mask)) + (<i32>mask & 64);
241+
export function __clz128(lo: u64, hi: i64): i32 {
242+
let h: u64 = <u64>hi; // reinterpret hi as unsigned
243+
if (h == 0) {
244+
// If hi is 0, the leading zeros are "64 plus however many are in lo"
245+
return 64 + <i32>i64.clz(lo);
246+
} else {
247+
// The top 64 bits are set => just measure their leading zeros
248+
return <i32>i64.clz(h);
249+
}
237250
}
238251

252+
/**
253+
* Count trailing zeros in a 128-bit integer [hi:lo], returning i32 in [0..128].
254+
* If both hi and lo are 0, returns 128.
255+
*
256+
* For i128 we typically treat hi as signed, but ctz is purely bitwise, so we
257+
* can pass it as u64 as well.
258+
*/
239259
// @ts-ignore: decorator
240260
@global @inline
241261
export function __ctz128(lo: u64, hi: u64): i32 {
242-
var mask: u64 = <i64>(lo ^ (lo - 1)) >> 63;
243-
return <i32>ctz((hi & mask) | (lo & ~mask)) + (<i32>mask & 64);
262+
if (lo == 0) {
263+
// Otherwise, ctz is 64 plus ctz(hi)
264+
return 64 + <i32>i64.ctz(hi);
265+
} else {
266+
// If the lower 64 bits are non-zero, measure ctz(lo)
267+
return <i32>i64.ctz(lo);
268+
}
244269
}
245270

246271
// @ts-ignore: decorator

0 commit comments

Comments
 (0)