openvibers/source.openvibe.games Commits

Provide 128-bit division builtins for clang-cl QuickJS (fix __udivti3)

CO Copilot committed 2 Jul 2026 · fc3408e44d · 2 files, +76 −0

The clang-cl QuickJS build linked into the MSVC client hit LNK2019
__udivti3 — clang lowers __int128 division in QuickJS BigInt to
compiler-rt builtins not present in the MSVC link. Add ovjs_win_builtins.c
(clang-cl, Windows-only) implementing __udivti3/__umodti3/__divti3/
__modti3 via binary long division, packed into libquickjs_openvibe.lib.
Linux keeps using libgcc's builtins (file is empty off clang/Windows).

Changes

ddiff --git a/sdk/openvibe/third_party/quickjs/ovjs_win_builtins.c b/sdk/openvibe/third_party/quickjs/ovjs_win_builtins.c
nnew file mode 100644
iindex 0000000..63a4901
---- /dev/null
++++ b/sdk/openvibe/third_party/quickjs/ovjs_win_builtins.c
@@@ -0,0 +1,74 @@
++/* ovjs_win_builtins.c — 128-bit integer division builtins for the Windows
++ * clang-cl QuickJS build.
++ *
++ * clang lowers __int128 division in QuickJS's BigInt code to calls into the
++ * compiler-rt builtins __udivti3/__umodti3/__divti3/__modti3. Those live in
++ * compiler-rt's builtins library, which isn't linked when packing clang-cl
++ * objects into an MSVC-linked DLL, producing LNK2019 __udivti3. We provide the
++ * routines here (compiled by clang-cl, which supports __int128) using binary
++ * long division — the operators themselves must NOT use `/` or `%` on __int128,
++ * as that would recurse back into these builtins.
++ *
++ * This file is only meaningful under clang on Windows; elsewhere it is empty. */
++#if defined(_WIN32) && defined(__clang__)
++
++typedef unsigned __int128 ovjs_u128;
++typedef signed __int128 ovjs_s128;
++
++/* Unsigned 128-bit divmod via shift-subtract long division. */
++static ovjs_u128 ovjs_udivmod128(ovjs_u128 num, ovjs_u128 den, ovjs_u128 *rem_out)
++{
++ if (den == 0) { /* undefined; match hardware trap behavior loosely */
++ if (rem_out) *rem_out = 0;
++ return ~(ovjs_u128)0;
++ }
++ ovjs_u128 quot = 0;
++ ovjs_u128 rem = 0;
++ for (int i = 127; i >= 0; --i) {
++ rem <<= 1;
++ rem |= (num >> i) & 1u;
++ if (rem >= den) {
++ rem -= den;
++ quot |= ((ovjs_u128)1) << i;
++ }
++ }
++ if (rem_out) *rem_out = rem;
++ return quot;
++}
++
++ovjs_u128 __udivti3(ovjs_u128 a, ovjs_u128 b)
++{
++ return ovjs_udivmod128(a, b, 0);
++}
++
++ovjs_u128 __umodti3(ovjs_u128 a, ovjs_u128 b)
++{
++ ovjs_u128 rem;
++ ovjs_udivmod128(a, b, &rem);
++ return rem;
++}
++
++ovjs_s128 __divti3(ovjs_s128 a, ovjs_s128 b)
++{
++ int neg = 0;
++ ovjs_u128 ua, ub, uq;
++ if (a < 0) { ua = (ovjs_u128)(-a); neg ^= 1; } else { ua = (ovjs_u128)a; }
++ if (b < 0) { ub = (ovjs_u128)(-b); neg ^= 1; } else { ub = (ovjs_u128)b; }
++ uq = ovjs_udivmod128(ua, ub, 0);
++ return neg ? -(ovjs_s128)uq : (ovjs_s128)uq;
++}
++
++ovjs_s128 __modti3(ovjs_s128 a, ovjs_s128 b)
++{
++ int neg = (a < 0);
++ ovjs_u128 ua, ub, ur;
++ ua = (a < 0) ? (ovjs_u128)(-a) : (ovjs_u128)a;
++ ub = (b < 0) ? (ovjs_u128)(-b) : (ovjs_u128)b;
++ ovjs_udivmod128(ua, ub, &ur);
++ return neg ? -(ovjs_s128)ur : (ovjs_s128)ur;
++}
++
++#else
++/* Keep the translation unit non-empty for non-clang/non-Windows toolchains. */
++typedef int ovjs_win_builtins_translation_unit_not_empty;
++#endif
ddiff --git a/tools/build-quickjs-lib-windows.ps1 b/tools/build-quickjs-lib-windows.ps1
iindex 522a31f..25bee57 100644
---- a/tools/build-quickjs-lib-windows.ps1
++++ b/tools/build-quickjs-lib-windows.ps1
@@@ -225,6 +225,8 @@ Say "msvc forced include=$MsvcCompat"
$sources = @('quickjs.c','libregexp.c','libunicode.c','cutils.c')
if (Test-Path (Join-Path $SdkQjs 'dtoa.c')) { $sources += 'dtoa.c' }
if (Test-Path (Join-Path $SdkQjs 'libbf.c')) { $sources += 'libbf.c' }
++# 128-bit division builtins (__udivti3 etc.) QuickJS BigInt needs under clang-cl.
++if (Test-Path (Join-Path $SdkQjs 'ovjs_win_builtins.c')) { $sources += 'ovjs_win_builtins.c' }
Remove-Item (Join-Path $Out '*.obj') -Force -ErrorAction SilentlyContinue
Remove-Item (Join-Path $Out 'libquickjs_openvibe.lib') -Force -ErrorAction SilentlyContinue