~ruther/guix-local

89e8c55f2b9e0d4f679561b96016b552a74e9534 — Ashvith Shetty a month ago 93e81e1
gnu: quickjs-ng: Resolve build issue with x86-32 platform.

* gnu/packages/javascript.scm (quickjs-ng):
[source]: Add quickjs-ng-64-bits-precision-on-i686.patch and
quickjs-ng-fix-atomics.pause-on-32-bit.patch patches.
[arguments]<#:tests?>: Enable them.
* quickjs-ng-64-bits-precision-on-i686.patch,
* gnu/packages/patches/quickjs-ng-fix-atomics.pause-on-32-bit.patch: New
files.
* gnu/local.mk (dist_patch_DATA): Add them.

Change-Id: I94be4c85725c9fe7780c1b2e2186411719051b56
Signed-off-by: Efraim Flashner <efraim@flashner.co.il>
M gnu/local.mk => gnu/local.mk +2 -0
@@ 2224,6 2224,8 @@ dist_patch_DATA =						\
  %D%/packages/patches/qtbase-5-use-TZDIR.patch			\
  %D%/packages/patches/qtscript-disable-tests.patch		\
  %D%/packages/patches/quagga-reproducible-build.patch          \
  %D%/packages/patches/quickjs-ng-64-bits-precision-on-i686.patch	\
  %D%/packages/patches/quickjs-ng-fix-atomics.pause-on-32-bit.patch	\
  %D%/packages/patches/quilt-grep-compat.patch 			\
  %D%/packages/patches/qmk-firmware-fix-hacker-dvorak.patch 	\
  %D%/packages/patches/qtwayland-dont-recreate-callbacks.patch	\

M gnu/packages/javascript.scm => gnu/packages/javascript.scm +5 -5
@@ 958,13 958,13 @@ wrappers.")
             (commit (string-append "v" version))))
       (file-name (git-file-name name version))
       (sha256
        (base32 "0mfk32zvvh6c9a9plp6ad07888g795lhdmal3jyaclyn2k5iig9i"))))
        (base32 "0mfk32zvvh6c9a9plp6ad07888g795lhdmal3jyaclyn2k5iig9i"))
       ;; Remove these patches on next release as they will be included.
       (patches (search-patches
                 "quickjs-ng-64-bits-precision-on-i686.patch"
                 "quickjs-ng-fix-atomics.pause-on-32-bit.patch"))))
    (arguments
     (list
      ;; Data model is ILP32 in 32bit, LP64 in 64bit
      ;; https://docs.oracle.com/cd/E19620-01/805-3024/lp64-1/index.html
      #:tests? (and (not (%current-target-system))
                    (target-64bit?))
      #:configure-flags
      #~(list "-DBUILD_SHARED_LIBS:BOOL=TRUE"
              "-DQJS_BUILD_EXAMPLES:BOOL=FALSE"

A gnu/packages/patches/quickjs-ng-64-bits-precision-on-i686.patch => gnu/packages/patches/quickjs-ng-64-bits-precision-on-i686.patch +211 -0
@@ 0,0 1,211 @@
From bd4ee689a854252665fd607758f078d930fbc4c8 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis <info@bnoordhuis.nl>
Date: Fri, 21 Nov 2025 11:56:28 +0100
Subject: [PATCH] Fix JS number rounding on x87 (#1244)

Disable 80-bits extended precision, use standard 64-bits precision.
The extra precision affects rounding and is user-observable, meaning
it causes test262 tests to fail.

The rounding mode is a per-CPU (or, more accurately, per-FPU) property,
and since quickjs is often used as a library, take care to tweak the FPU
control word before sensitive operations, and restore it afterwards.

Only affects x87. SSE is IEEE-754 conforming.

Fixes: https://github.com/quickjs-ng/quickjs/issues/1236
---
 .github/workflows/ci.yml |  4 ++--
 cutils.h                 | 19 +++++++++++++++++++
 quickjs.c                | 34 +++++++++++++++++++++++++++++-----
 3 files changed, 50 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d1efee1b9..c722b517d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -58,7 +58,7 @@ jobs:
           - { os: ubuntu-latest, configType: asan+ubsan, runTest262: true }
           - { os: ubuntu-latest, configType: msan }
           - { os: ubuntu-latest, configType: tcc }
-          - { os: ubuntu-latest, arch: x86 }
+          - { os: ubuntu-latest, arch: x86, runTest262: true }
           - { os: ubuntu-latest, arch: riscv64 }
           - { os: ubuntu-latest, arch: s390x }
 
@@ -97,7 +97,7 @@ jobs:
         uses: jirutka/setup-alpine@v1
         with:
           arch: ${{ matrix.config.arch }}
-          packages: "build-base make cmake"
+          packages: "build-base make cmake git"
 
       - name: uname
         run: uname -a
diff --git a/cutils.h b/cutils.h
index 5129c3cb2..f5d2a8bce 100644
--- a/cutils.h
+++ b/cutils.h
@@ -634,6 +634,25 @@ int js_thread_join(js_thread_t thrd);
 
 #endif /* !defined(EMSCRIPTEN) && !defined(__wasi__) */
 
+// JS requires strict rounding behavior. Turn on 64-bits double precision
+// and disable x87 80-bits extended precision for intermediate floating-point
+// results. 0x300 is extended  precision, 0x200 is double precision.
+// Note that `*&cw` in the asm constraints looks redundant but isn't.
+#if defined(__i386__) && !defined(_MSC_VER)
+#define JS_X87_FPCW_SAVE_AND_ADJUST(cw)                                     \
+    unsigned short cw;                                                      \
+    __asm__ __volatile__("fnstcw %0" : "=m"(*&cw));                         \
+    do {                                                                    \
+        unsigned short t = 0x200 | (cw & ~0x300);                           \
+        __asm__ __volatile__("fldcw %0" : /*empty*/ : "m"(*&t));            \
+    } while (0)
+#define JS_X87_FPCW_RESTORE(cw)                                             \
+    __asm__ __volatile__("fldcw %0" : /*empty*/ : "m"(*&cw))
+#else
+#define JS_X87_FPCW_SAVE_AND_ADJUST(cw)
+#define JS_X87_FPCW_RESTORE(cw)
+#endif
+
 #ifdef __cplusplus
 } /* extern "C" { */
 #endif
diff --git a/quickjs.c b/quickjs.c
index 3970bb247..7900c5e3d 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -13395,12 +13395,17 @@ static int js_is_array(JSContext *ctx, JSValueConst val)
 
 static double js_math_pow(double a, double b)
 {
+    double d;
+
     if (unlikely(!isfinite(b)) && fabs(a) == 1) {
         /* not compatible with IEEE 754 */
-        return NAN;
+        d = NAN;
     } else {
-        return pow(a, b);
+        JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
+        d = pow(a, b);
+        JS_X87_FPCW_RESTORE(fpcw);
     }
+    return d;
 }
 
 JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
@@ -13820,11 +13825,15 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
             }
             break;
         case OP_div:
+            JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
             sp[-2] = js_number((double)v1 / (double)v2);
+            JS_X87_FPCW_RESTORE(fpcw);
             return 0;
         case OP_mod:
             if (v1 < 0 || v2 <= 0) {
+                JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
                 sp[-2] = js_number(fmod(v1, v2));
+                JS_X87_FPCW_RESTORE(fpcw);
                 return 0;
             } else {
                 v = (int64_t)v1 % (int64_t)v2;
@@ -13888,6 +13897,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
         if (JS_ToFloat64Free(ctx, &d2, op2))
             goto exception;
     handle_float64:
+        JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
         switch(op) {
         case OP_sub:
             dr = d1 - d2;
@@ -13907,6 +13917,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
         default:
             abort();
         }
+        JS_X87_FPCW_RESTORE(fpcw);
         sp[-2] = js_float64(dr);
     }
     return 0;
@@ -14023,7 +14034,9 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
         }
         if (JS_ToFloat64Free(ctx, &d2, op2))
             goto exception;
+        JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
         sp[-2] = js_float64(d1 + d2);
+        JS_X87_FPCW_RESTORE(fpcw);
     }
     return 0;
  exception:
@@ -17998,8 +18011,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                     sp[-2] = js_int32(r);
                     sp--;
                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+                    JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
                     sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) +
                                         JS_VALUE_GET_FLOAT64(op2));
+                    JS_X87_FPCW_RESTORE(fpcw);
                     sp--;
                 } else {
                 add_slow:
@@ -18066,8 +18081,10 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                     sp[-2] = js_int32(r);
                     sp--;
                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+                    JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
                     sp[-2] = js_float64(JS_VALUE_GET_FLOAT64(op1) -
                                         JS_VALUE_GET_FLOAT64(op2));
+                    JS_X87_FPCW_RESTORE(fpcw);
                     sp--;
                 } else {
                     goto binary_arith_slow;
@@ -18098,7 +18115,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
                     sp[-2] = js_int32(r);
                     sp--;
                 } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+                    JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
                     d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
+                    JS_X87_FPCW_RESTORE(fpcw);
                 mul_fp_res:
                     sp[-2] = js_float64(d);
                     sp--;
@@ -52277,7 +52296,9 @@ static double set_date_fields(double fields[minimum_length(7)], int is_local) {
     int yi, mi, i;
     int64_t days;
     volatile double temp;  /* enforce evaluation order */
+    double d = NAN;
 
+    JS_X87_FPCW_SAVE_AND_ADJUST(fpcw);
     /* emulate 21.4.1.15 MakeDay ( year, month, date ) */
     y = fields[0];
     m = fields[1];
@@ -52287,7 +52308,7 @@ static double set_date_fields(double fields[minimum_length(7)], int is_local) {
     if (mn < 0)
         mn += 12;
     if (ym < -271821 || ym > 275760)
-        return NAN;
+        goto out;
 
     yi = ym;
     mi = mn;
@@ -52319,14 +52340,17 @@ static double set_date_fields(double fields[minimum_length(7)], int is_local) {
     /* emulate 21.4.1.16 MakeDate ( day, time ) */
     tv = (temp = day * 86400000) + time;   /* prevent generation of FMA */
     if (!isfinite(tv))
-        return NAN;
+        goto out;
 
     /* adjust for local time and clip */
     if (is_local) {
         int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv;
         tv += getTimezoneOffset(ti) * 60000;
     }
-    return time_clip(tv);
+    d = time_clip(tv);
+out:
+    JS_X87_FPCW_RESTORE(fpcw);
+    return d;
 }
 
 static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,

A gnu/packages/patches/quickjs-ng-fix-atomics.pause-on-32-bit.patch => gnu/packages/patches/quickjs-ng-fix-atomics.pause-on-32-bit.patch +26 -0
@@ 0,0 1,26 @@
From c211485e6f6c9fcd0ea9f5aa1943f8c95fdd186b Mon Sep 17 00:00:00 2001
From: Ben Noordhuis <info@bnoordhuis.nl>
Date: Sun, 9 Nov 2025 21:28:14 +0100
Subject: [PATCH] Fix Atomics.pause on 32 bits platforms

Use JS_VALUE_GET_NORM_TAG to get the type tag, not JS_VALUE_GET_TAG.
The latter doesn't work correctly with NaN boxing.

Refs: https://github.com/quickjs-ng/quickjs/issues/986#issuecomment-3508547332
---
 quickjs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/quickjs.c b/quickjs.c
index 4fb9f95b7..3970bb247 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -57112,7 +57112,7 @@ static JSValue js_atomics_pause(JSContext *ctx, JSValueConst this_obj,
     double d;
 
     if (argc > 0) {
-        switch (JS_VALUE_GET_TAG(argv[0])) {
+        switch (JS_VALUE_GET_NORM_TAG(argv[0])) {
         case JS_TAG_FLOAT64: // accepted if and only if fraction == 0.0
             d = JS_VALUE_GET_FLOAT64(argv[0]);
             if (isfinite(d))