[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 6 commits: Bump bytestring submodule to 0.11.5.1

Marge Bot (@marge-bot) gitlab at gitlab.haskell.org
Mon Aug 7 07:57:22 UTC 2023



Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC


Commits:
43578d60 by Matthew Craven at 2023-08-05T01:05:36-04:00
Bump bytestring submodule to 0.11.5.1

- - - - -
91353622 by Ben Gamari at 2023-08-05T01:06:13-04:00
Initial commit of Note [Thunks, blackholes, and indirections]

This Note attempts to summarize the treatment of thunks, thunk update,
and indirections.

This fell out of work on #23185.

- - - - -
8d686854 by sheaf at 2023-08-05T01:06:54-04:00
Remove zonk in tcVTA

This removes the zonk in GHC.Tc.Gen.App.tc_inst_forall_arg and its
accompanying Note [Visible type application zonk]. Indeed, this zonk
is no longer necessary, as we no longer maintain the invariant that
types are well-kinded without zonking; only that typeKind does not
crash; see Note [The Purely Kinded Type Invariant (PKTI)].

This commit removes this zonking step (as well as a secondary zonk),
and replaces the aforementioned Note with the explanatory
Note [Type application substitution], which justifies why the
substitution performed in tc_inst_forall_arg remains valid without
this zonking step.

Fixes #23661

- - - - -
19dea673 by Ben Gamari at 2023-08-05T01:07:30-04:00
Bump nofib submodule

Ensuring that nofib can be build using the same range of bootstrap
compilers as GHC itself.

- - - - -
aa07402e by Luite Stegeman at 2023-08-05T23:15:55+09:00
JS: Improve compatibility with recent emsdk

The JavaScript code in libraries/base/jsbits/base.js had some
hardcoded offsets for fields in structs, because we expected
the layout of the data structures to remain unchanged. Emsdk
3.1.42 changed the layout of the stat struct, breaking this
assumption, and causing code in .hsc files accessing the
stat struct to fail.

This patch improves compatibility with recent emsdk by
removing the assumption that data layouts stay unchanged:

    1. offsets of fields in structs used by JavaScript code are
       now computed by the configure script, so both the .js and
       .hsc files will automatically use the new layout if anything
       changes.
    2. the distrib/configure script checks that the emsdk version
       on a user's system is the same version that a bindist was
       booted with, to avoid data layout inconsistencies

See #23641

- - - - -
9574d51b by Luite Stegeman at 2023-08-07T03:57:00-04:00
JS: Fix missing local variable declarations

This fixes some missing local variable declarations that were
found by running the testsuite in strict mode.

Fixes #23775

- - - - -


23 changed files:

- .gitlab-ci.yml
- compiler/GHC/JS/Make.hs
- compiler/GHC/StgToJS/Expr.hs
- compiler/GHC/Tc/Gen/App.hs
- compiler/GHC/Utils/Binary.hs
- configure.ac
- distrib/configure.ac.in
- hadrian/src/Settings/Warnings.hs
- libraries/base/System/Posix/Internals.hs
- libraries/base/aclocal.m4
- libraries/base/configure.ac
- libraries/base/jsbits/base.js
- libraries/bytestring
- + m4/emsdk_version.m4
- nofib
- rts/Updates.h
- rts/js/arith.js
- rts/js/environment.js
- rts/js/thread.js
- testsuite/tests/ghci/scripts/T9881.stdout
- testsuite/tests/ghci/scripts/ghci025.stdout
- testsuite/tests/javascript/js-callback03.hs
- testsuite/tests/programs/seward-space-leak/test.T


Changes:

=====================================
.gitlab-ci.yml
=====================================
@@ -401,7 +401,7 @@ hadrian-multi:
     # workaround for docker permissions
     - sudo chown ghc:ghc -R .
   variables:
-    GHC_FLAGS: -Werror
+    GHC_FLAGS: "-Werror -Wwarn=deprecations"
     CONFIGURE_ARGS: --enable-bootstrap-with-devel-snapshot
   tags:
     - x86_64-linux


=====================================
compiler/GHC/JS/Make.hs
=====================================
@@ -268,7 +268,7 @@ jVar f = UnsatBlock . IS $ do
            (block, is) <- runIdentSupply $ toSat_ f []
            let addDecls (BlockStat ss) =
                   BlockStat $ map decl is ++ ss
-               addDecls x = x
+               addDecls x = BlockStat (map decl is ++ [x])
            return $ addDecls block
 
 jFunction :: Ident -> [Ident] -> JStat -> JStat


=====================================
compiler/GHC/StgToJS/Expr.hs
=====================================
@@ -509,7 +509,7 @@ optimizeFree offset ids = do
 allocCls :: Maybe JStat -> [(Id, CgStgRhs)] -> G JStat
 allocCls dynMiddle xs = do
    (stat, dyn) <- partitionEithers <$> mapM toCl xs
-   ac <- allocDynAll True dynMiddle dyn
+   ac <- allocDynAll False dynMiddle dyn
    pure (mconcat stat <> ac)
   where
     -- left = static, right = dynamic


=====================================
compiler/GHC/Tc/Gen/App.hs
=====================================
@@ -840,19 +840,14 @@ tc_inst_forall_arg conc_tvs (tvb, inner_ty) hs_ty
              -- Example: user wrote e.g. (#,#) @(F Bool) for a type family F.
              -- Emit [W] F Bool ~ kappa[conc] and pretend the user wrote (#,#) @kappa.
              do { mco <- unifyConcrete (occNameFS $ getOccName $ tv_nm) conc ty_arg0
-                ; let ty_arg = case mco of { MRefl -> ty_arg0; MCo co -> coercionRKind co }
-                ; liftZonkM $ zonkTcType ty_arg }
-                -- (zonk here to match the zonk below, because unifyConcrete can
-                -- perform unification)
-
-       ; inner_ty <- liftZonkM $ zonkTcType inner_ty
-             -- See Note [Visible type application zonk]
+                ; return $ case mco of { MRefl -> ty_arg0; MCo co -> coercionRKind co } }
 
        ; let fun_ty    = mkForAllTy tvb inner_ty
              in_scope  = mkInScopeSet (tyCoVarsOfTypes [fun_ty, ty_arg])
              insted_ty = substTyWithInScope in_scope [tv] [ty_arg] inner_ty
-                         -- NB: tv and ty_arg have the same kind, so this
-                         --     substitution is kind-respecting
+               -- This substitution is well-kinded even when inner_ty
+               -- is not fully zonked, because ty_arg is fully zonked.
+               -- See Note [Type application substitution].
 
        ; traceTc "tc_inst_forall_arg (VTA/VDQ)" (
                   vcat [ text "fun_ty" <+> ppr fun_ty
@@ -1141,35 +1136,39 @@ Downside: the typechecked term has lost its visible type arguments; we
 don't even kind-check them.  But let's jump that bridge if we come to
 it.  Meanwhile, let's not crash!
 
-
-Note [Visible type application zonk]
+Note [Type application substitution]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* Substitutions should be kind-preserving, so we need kind(tv) = kind(ty_arg).
-
-* tcHsTypeApp only guarantees that
-    - ty_arg is zonked
-    - kind(zonk(tv)) = kind(ty_arg)
-  (checkExpectedKind zonks as it goes).
-
-So we must zonk inner_ty as well, to guarantee consistency between zonk(tv)
-and inner_ty. Otherwise we can build an ill-kinded type. An example was #14158,
-where we had:
-   id :: forall k. forall (cat :: k -> k -> *). forall (a :: k). cat a a
-and we had the visible type application
-  id @(->)
-
-* We instantiated k := kappa, yielding
-    forall (cat :: kappa -> kappa -> *). forall (a :: kappa). cat a a
-* Then we called tcHsTypeApp (->) with expected kind (kappa -> kappa -> *).
-* That instantiated (->) as ((->) q1 q1), and unified kappa := q1,
-  Here q1 :: RuntimeRep
-* Now we substitute
-     cat  :->  (->) q1 q1 :: TYPE q1 -> TYPE q1 -> *
-  but we must first zonk the inner_ty to get
-      forall (a :: TYPE q1). cat a a
-  so that the result of substitution is well-kinded
-  Failing to do so led to #14158.
-
+In `tc_inst_forall_arg`, suppose we are checking a visible type
+application `f @hs_ty`, where `f :: forall (a :: k). body`.  We will:
+  * Compute `ty <- tcHsTypeApp hs_ty k`
+  * Then substitute `a :-> ty` in `body`.
+Now, you might worry that `a` might not have the same kind as `ty`, so that the
+substitution isn't kind-preserving.  How can that happen?  The kinds will
+definitely be the same after zonking, and `ty` will be zonked (as this is
+a postcondition of `tcHsTypeApp`). But the function type `forall a. body`
+might not be fully zonked (hence the worry).
+
+But it's OK!  During type checking, we don't require types to be well-kinded (without
+zonking); we only require them to satsisfy the Purely Kinded Type Invariant (PKTI).
+See Note [The Purely Kinded Type Invariant (PKTI)] in GHC.Tc.Gen.HsType.
+
+In the case of a type application:
+  * `forall a. body` satisfies the PKTI
+  * `ty` is zonked
+  * If we substitute a fully-zonked thing into an un-zonked Type that
+    satisfies the PKTI, the result still satisfies the PKTI.
+
+This last statement isn't obvious, but read
+Note [The Purely Kinded Type Invariant (PKTI)] in GHC.Tc.Gen.HsType.
+The tricky case is when `body` contains an application of the form `a b1 ... bn`,
+and we substitute `a :-> ty` where `ty` has fewer arrows in its kind than `a` does.
+That can't happen: the call `tcHsTypeApp hs_ty k` would have rejected the
+type application as ill-kinded.
+
+Historical remark: we used to require a stronger invariant than the PKTI,
+namely that all types are well-kinded prior to zonking. In that context, we did
+need to zonk `body` before performing the substitution above. See test case
+#14158, as well as the discussion in #23661.
 -}
 
 {- *********************************************************************


=====================================
compiler/GHC/Utils/Binary.hs
=====================================
@@ -1240,13 +1240,13 @@ putBS :: BinHandle -> ByteString -> IO ()
 putBS bh bs =
   BS.unsafeUseAsCStringLen bs $ \(ptr, l) -> do
     put_ bh l
-    putPrim bh l (\op -> BS.memcpy op (castPtr ptr) l)
+    putPrim bh l (\op -> copyBytes op (castPtr ptr) l)
 
 getBS :: BinHandle -> IO ByteString
 getBS bh = do
   l <- get bh :: IO Int
   BS.create l $ \dest -> do
-    getPrim bh l (\src -> BS.memcpy dest src l)
+    getPrim bh l (\src -> copyBytes dest src l)
 
 instance Binary ByteString where
   put_ bh f = putBS bh f


=====================================
configure.ac
=====================================
@@ -461,6 +461,10 @@ FP_SET_CFLAGS_C99([CC_STAGE0],[CONF_CC_OPTS_STAGE0],[CONF_CPP_OPTS_STAGE0])
 FP_SET_CFLAGS_C99([CC],[CONF_CC_OPTS_STAGE1],[CONF_CPP_OPTS_STAGE1])
 FP_SET_CFLAGS_C99([CC],[CONF_CC_OPTS_STAGE2],[CONF_CPP_OPTS_STAGE2])
 
+dnl ** Do we have a compatible emsdk version?
+dnl --------------------------------------------------------------
+EMSDK_VERSION("3.1.20", "", "")
+
 dnl ** Which ld to use
 dnl --------------------------------------------------------------
 AC_ARG_VAR(LD,[Use as the path to ld. See also --disable-ld-override.])
@@ -531,6 +535,9 @@ sUPPORTED_LLVM_VERSION_MAX=$(echo \($LlvmMaxVersion\) | sed 's/\./,/')
 AC_DEFINE_UNQUOTED([sUPPORTED_LLVM_VERSION_MIN], ${sUPPORTED_LLVM_VERSION_MIN}, [The minimum supported LLVM version number])
 AC_DEFINE_UNQUOTED([sUPPORTED_LLVM_VERSION_MAX], ${sUPPORTED_LLVM_VERSION_MAX}, [The maximum supported LLVM version number])
 
+ConfiguredEmsdkVersion="${EmsdkVersion}"
+AC_SUBST([ConfiguredEmsdkVersion])
+
 dnl ** Which LLVM llc to use?
 dnl --------------------------------------------------------------
 AC_ARG_VAR(LLC,[Use as the path to LLVM's llc [default=autodetect]])


=====================================
distrib/configure.ac.in
=====================================
@@ -195,6 +195,10 @@ dnl Identify C++ standard library flavour and location
 FP_FIND_CXX_STD_LIB
 AC_CONFIG_FILES([mk/system-cxx-std-lib-1.0.conf])
 
+# Check that we have the same emsdk version as the one we were built with.
+ConfiguredEmsdkVersion=@ConfiguredEmsdkVersion@
+EMSDK_VERSION("", "", ${ConfiguredEmsdkVersion})
+
 dnl ** Set up the variables for the platform in the settings file.
 dnl May need to use gcc to find platform details.
 dnl --------------------------------------------------------------


=====================================
hadrian/src/Settings/Warnings.hs
=====================================
@@ -53,10 +53,12 @@ ghcWarningsArgs = do
         , package primitive    ? pure [ "-Wno-unused-imports"
                                       , "-Wno-deprecations" ]
         , package rts          ? pure [ "-Wcpp-undef" ]
+        , package text         ? pure [ "-Wno-deprecations" ]
         , package terminfo     ? pure [ "-Wno-unused-imports" ]
         , package transformers ? pure [ "-Wno-unused-matches"
                                       , "-Wno-unused-imports"
                                       , "-Wno-redundant-constraints"
                                       , "-Wno-orphans" ]
+        , package unix         ? pure [ "-Wno-deprecations" ]
         , package win32        ? pure [ "-Wno-trustworthy-safe" ]
         , package xhtml        ? pure [ "-Wno-unused-imports" ] ] ]


=====================================
libraries/base/System/Posix/Internals.hs
=====================================
@@ -561,7 +561,7 @@ foreign import javascript unsafe "h$base_tcgetattr"
    c_tcgetattr :: CInt -> Ptr CTermios -> IO CInt
 foreign import javascript unsafe "h$base_tcsetattr"
    c_tcsetattr :: CInt -> CInt -> Ptr CTermios -> IO CInt
-foreign import javascript unsafe "h$base_utime" -- should this be async?
+foreign import javascript interruptible "h$base_utime"
    c_utime :: CString -> Ptr CUtimbuf -> IO CInt
 foreign import javascript interruptible "h$base_waitpid"
    c_waitpid :: CPid -> Ptr CInt -> CInt -> IO CPid
@@ -594,7 +594,7 @@ s_isdir cm = c_s_isdir cm /= 0
 s_isfifo :: CMode -> Bool
 s_isfifo cm = c_s_isfifo cm /= 0
 
-foreign import javascript unsafe "(() => { return h$base_sizeof_stat; })" sizeof_stat :: Int
+foreign import javascript unsafe "h$base_sizeof_stat" sizeof_stat :: Int
 foreign import javascript unsafe "h$base_st_mtime"    st_mtime :: Ptr CStat -> IO CTime
 foreign import javascript unsafe "h$base_st_size"     st_size :: Ptr CStat -> IO Int64
 foreign import javascript unsafe "h$base_st_mode"     st_mode :: Ptr CStat -> IO CMode


=====================================
libraries/base/aclocal.m4
=====================================
@@ -14,6 +14,32 @@ AC_DEFUN([FP_COMPUTE_INT],
 ])# FP_COMPUTE_INT
 
 
+# FP_COMPUTE_OFFSET(VARIABLE, TYPE, MEMBER, INCLUDES)
+# ---------------------------------------------------
+# Assign VARIABLE the offset of MEMBER in struct TYPE using INCLUDES
+# for compilation. If compilation fails, VARIABLE is set to -1. Works for
+# cross-compilation, too.
+AC_DEFUN([FP_COMPUTE_OFFSET],
+[AC_MSG_CHECKING([offset of [$2].[$3]])
+FP_COMPUTE_INT([$1], [(offsetof(struct [$2], [$3]))], [$4
+#include <stddef.h>], AC_MSG_ERROR([could not determine offset of $2.$3]))
+AC_MSG_RESULT($[$1])
+AC_DEFINE_UNQUOTED([$1], $[$1], [Offset of $2.$3])
+]) # FP_COMPUTE_OFFSET
+
+# FP_COMPUTE_SIZE(VARIABLE, TYPE, MEMBER, INCLUDES)
+# ---------------------------------------------------
+# Assign VARIABLE the offset of MEMBER in struct TYPE using INCLUDES
+# for compilation. If compilation fails, VARIABLE is set to -1. Works for
+# cross-compilation, too.
+AC_DEFUN([FP_COMPUTE_SIZE],
+[AC_MSG_CHECKING([size of [$2].[$3]])
+FP_COMPUTE_INT([$1], [(sizeof(((struct [$2] *)0)->[$3]))], [$4
+#include <stddef.h>], AC_MSG_ERROR([could not determine size of $2.$3]))
+AC_MSG_RESULT($[$1])
+AC_DEFINE_UNQUOTED([$1], $[$1], [Size of $2.$3])
+]) # FP_COMPUTE_SIZE
+
 # FP_CHECK_CONST(EXPRESSION, [INCLUDES = DEFAULT-INCLUDES], [VALUE-IF-FAIL = -1])
 # -------------------------------------------------------------------------------
 # Defines CONST_EXPRESSION to the value of the compile-time EXPRESSION, using


=====================================
libraries/base/configure.ac
=====================================
@@ -126,6 +126,45 @@ AC_ARG_WITH([iconv-libraries],
 AC_SUBST(ICONV_INCLUDE_DIRS)
 AC_SUBST(ICONV_LIB_DIRS)
 
+
+# Compute offsets/sizes used by jsbits/base.js
+if test "$host" = "javascript-ghcjs"
+then
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_MODE],    [stat], [st_mode],    [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_DEV],     [stat], [st_dev],     [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_UID],     [stat], [st_uid],     [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_GID],     [stat], [st_gid],     [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_NLINK],   [stat], [st_nlink],   [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_RDEV],    [stat], [st_rdev],    [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_SIZE],    [stat], [st_size],    [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_BLKSIZE], [stat], [st_blksize], [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_BLOCKS],  [stat], [st_blocks],  [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_INO],     [stat], [st_ino],     [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_ATIME],   [stat], [st_atime],   [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_MTIME],   [stat], [st_mtime],   [#include <sys/stat.h>])
+  FP_COMPUTE_OFFSET([OFFSET_STAT_ST_CTIME],   [stat], [st_ctime],   [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_MODE],    [stat], [st_mode],    [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_DEV],     [stat], [st_dev],     [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_UID],     [stat], [st_uid],     [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_GID],     [stat], [st_gid],     [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_NLINK],   [stat], [st_nlink],   [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_RDEV],    [stat], [st_rdev],    [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_SIZE],    [stat], [st_size],    [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_BLKSIZE], [stat], [st_blksize], [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_BLOCKS],  [stat], [st_blocks],  [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_INO],     [stat], [st_ino],     [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_ATIME],   [stat], [st_atime],   [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_MTIME],   [stat], [st_mtime],   [#include <sys/stat.h>])
+  FP_COMPUTE_SIZE([SIZEOF_STAT_ST_CTIME],   [stat], [st_ctime],   [#include <sys/stat.h>])
+  AC_CHECK_SIZEOF([struct stat], [], [#include <sys/stat.h>])
+
+  FP_COMPUTE_OFFSET([OFFSET_UTIMBUF_ACTIME],  [utimbuf], [actime],  [#include <utime.h>])
+  FP_COMPUTE_OFFSET([OFFSET_UTIMBUF_MODTIME], [utimbuf], [modtime], [#include <utime.h>])
+  FP_COMPUTE_SIZE([SIZEOF_UTIMBUF_ACTIME],  [utimbuf], [actime],  [#include <utime.h>])
+  FP_COMPUTE_SIZE([SIZEOF_UTIMBUF_MODTIME], [utimbuf], [modtime], [#include <utime.h>])
+  AC_CHECK_SIZEOF([struct utimbuf], [], [#include <utime.h>])
+fi
+
 # map standard C types and ISO types to Haskell types
 FPTOOLS_CHECK_HTYPE(char)
 FPTOOLS_CHECK_HTYPE(signed char)


=====================================
libraries/base/jsbits/base.js
=====================================
@@ -435,18 +435,8 @@ function h$base_utime(file, file_off, timbuf, timbuf_off, c) {
             if(err) {
                 h$handleErrnoC(err, 0, -1, c); // fixme
             } else {
-                h$long_from_number(fs.atime.getTime(), (h,l) => {
-                    timbuf.i3[0] = h;
-                    timbuf.i3[1] = l;
-                  });
-                h$long_from_number(fs.mtime.getTime(), (h,l) => {
-                    timbuf.i3[2] = h;
-                    timbuf.i3[3] = l;
-                  });
-                h$long_from_number(fs.ctime.getTime(), (h,l) => {
-                    timbuf.i3[4] = h;
-                    timbuf.i3[5] = l;
-                  });
+                h$base_store_field_number(timbuf, timbuf_off, OFFSET_UTIMBUF_ACTIME, SIZEOF_UTIMBUF_ACTIME, fs.atime.getTime());
+                h$base_store_field_number(timbuf, timbuf_off, OFFSET_UTIMBUF_MODTIME, SIZEOF_UTIMBUF_MODTIME, fs.mtime.getTime());
                 c(0);
             }
         });
@@ -507,85 +497,96 @@ function h$base_c_fcntl_lock(fd,cmd,ptr,ptr_o) {
 // memory locations of structs directly. For more information see:
 // https://gitlab.haskell.org/ghc/ghc/-/issues/22573
 function h$base_fillStat(fs, b, off) {
-    if(off%4) throw "h$base_fillStat: not aligned";
+    if(off%4) throw new Error("h$base_fillStat: not aligned");
     var o = off>>2;
 
-    b.i3[o+0] = fs.dev;
-    b.i3[o+1] = 0; // __st_dev_padding;
-    b.i3[o+2] = 0; // __st_ino_truncated;
-    b.i3[o+3] = fs.mode;
-    h$long_from_number(fs.nlink, (h,l) => {
-      b.i3[o+4] = h;
-      b.i3[o+5] = l;
-    });
-    b.i3[o+6] = fs.uid;
-    b.i3[o+7] = fs.gid;
-    b.i3[o+8] = fs.rdev;
-    b.i3[o+9] = 0; // __st_rdev_padding;
-    h$long_from_number(fs.size, (h,l) => {
-      b.i3[o+10] = h;
-      b.i3[o+11] = l;
-    });
-    b.i3[o+12] = fs.blksize;
-    b.i3[o+13] = fs.blocks;
-    atimeS = Math.floor(fs.atimeMs/1000);
-    h$long_from_number(atimeS, (h,l) => {
-      b.i3[o+14] = h;
-      b.i3[o+15] = l;
-    });
-    atimeNs = (fs.atimeMs/1000 - atimeS) * 1000000000;
-    h$long_from_number(atimeNs, (h,l) => {
-      b.i3[o+16] = h;
-      b.i3[o+17] = l;
-    });
-    mtimeS = Math.floor(fs.mtimeMs/1000);
-    h$long_from_number(mtimeS, (h,l) => {
-      b.i3[o+18] = h;
-      b.i3[o+19] = l;
-    });
-    mtimeNs = (fs.mtimeMs/1000 - mtimeS) * 1000000000;
-    h$long_from_number(mtimeNs, (h,l) => {
-      b.i3[o+20] = h;
-      b.i3[o+21] = l;
-    });
-    ctimeS = Math.floor(fs.ctimeMs/1000);
-    h$long_from_number(ctimeS, (h,l) => {
-      b.i3[o+22] = h;
-      b.i3[o+23] = l;
-    });
-    ctimeNs = (fs.ctimeMs/1000 - ctimeS) * 1000000000;
-    h$long_from_number(ctimeNs, (h,l) => {
-      b.i3[o+24] = h;
-      b.i3[o+25] = l;
-    });
-    h$long_from_number(fs.ino, (h,l) => {
-      b.i3[o+26] = h;
-      b.i3[o+27] = l;
-    });
+    // clear memory
+    for(var i=0;i<(SIZEOF_STRUCT_STAT>>2);i++) {
+        b.i3[o+i] = 0;
+    }
+
+    // fill struct
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_DEV,     SIZEOF_STAT_ST_DEV,     fs.dev);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_MODE,    SIZEOF_STAT_ST_MODE,    fs.mode);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_NLINK,   SIZEOF_STAT_ST_NLINK,   fs.nlink);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_UID,     SIZEOF_STAT_ST_UID,     fs.uid);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_GID,     SIZEOF_STAT_ST_GID,     fs.gid);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_RDEV,    SIZEOF_STAT_ST_RDEV,    fs.rdev);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_SIZE,    SIZEOF_STAT_ST_SIZE,    fs.size);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_BLKSIZE, SIZEOF_STAT_ST_BLKSIZE, fs.blksize);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_BLOCKS,  SIZEOF_STAT_ST_BLOCKS,  fs.blocks);
+    h$base_store_field_number(b, off, OFFSET_STAT_ST_INO,     SIZEOF_STAT_ST_INO,     fs.ino);
+
+    var atimeS = Math.floor(fs.atimeMs/1000);
+    var atimeNs = (fs.atimeMs/1000 - atimeS) * 1000000000;
+    h$base_store_field_number_2(b, off, OFFSET_STAT_ST_ATIME, SIZEOF_STAT_ST_ATIME,  atimeS, atimeNs);
+    var mtimeS = Math.floor(fs.mtimeMs/1000);
+    var mtimeNs = (fs.mtimeMs/1000 - mtimeS) * 1000000000;
+    h$base_store_field_number_2(b, off, OFFSET_STAT_ST_MTIME, SIZEOF_STAT_ST_MTIME,  mtimeS, mtimeNs);
+    var ctimeS = Math.floor(fs.ctimeMs/1000);
+    var ctimeNs = (fs.ctimeMs/1000 - ctimeS) * 1000000000;
+    h$base_store_field_number_2(b, off, OFFSET_STAT_ST_CTIME, SIZEOF_STAT_ST_CTIME,  ctimeS, ctimeNs);
 }
 #endif
 
-// [mode,size1,size2,mtime1,mtime2,dev,ino1,ino2,uid,gid] all 32 bit
-/** @const */ var h$base_sizeof_stat = 112;
+function h$base_store_field_number(ptr, ptr_off, field_off, field_size, val) {
+    if(ptr_off%4) throw new Error("ptr not aligned");
+    if(field_off%4) throw new Error("field not aligned");
+    if(typeof val !== 'number') throw new Error("not a number: " + val);
+    if(field_size === 4) {
+        ptr.i3[(ptr_off>>2)+(field_off>>2)] = val;
+    } else if(field_size === 8) {
+        h$long_from_number(val, (h,l) => {
+            ptr.i3[(ptr_off>>2)+(field_off>>2)] = h;
+            ptr.i3[(ptr_off>>2)+(field_off>>2)+1] = l;
+        });
+    } else {
+        throw new Error("unsupported field size: " + field_size);
+    }
+}
+
+function h$base_store_field_number_2(ptr, ptr_off, field_off, field_size, val1, val2) {
+    if(field_size%2) throw new Error("unsupported field size: " + field_size);
+    var half_field_size = field_size>>1;
+    h$base_store_field_number(ptr, ptr_off, field_off, half_field_size, val1);
+    h$base_store_field_number(ptr, ptr_off, field_off+half_field_size, half_field_size, val2);
+}
+
+function h$base_return_field(ptr, ptr_off, field_off, field_size) {
+    if(ptr_off%4) throw new Error("ptr not aligned");
+    if(field_off%4) throw new Error("field not aligned");
+    if(field_size === 4) {
+        return ptr.i3[(ptr_off>>2) + (field_off>>2)];
+    } else if(field_size === 8) {
+        RETURN_UBX_TUP2(ptr.i3[(ptr_off>>2) + (field_off>>2)], ptr.i3[(ptr_off>>2) + (field_off>>2)+1]);
+    } else {
+        throw new Error("unsupported field size: " + field_size);
+    }
+}
+
+function h$base_sizeof_stat() {
+    return SIZEOF_STRUCT_STAT;
+}
 
 function h$base_st_mtime(stat, stat_off) {
-    RETURN_UBX_TUP2(stat.i3[(stat_off>>2)+18], stat.i3[(stat_off>>2)+19]);
+    // XXX should we use the nanoseconds?
+    return h$base_return_field(stat, stat_off, OFFSET_STAT_ST_MTIME, SIZEOF_STAT_ST_MTIME);
 }
 
 function h$base_st_size(stat, stat_off) {
-    RETURN_UBX_TUP2(stat.i3[(stat_off>>2)+10], stat.i3[(stat_off>>2)+11]);
+    return h$base_return_field(stat, stat_off, OFFSET_STAT_ST_SIZE, SIZEOF_STAT_ST_SIZE);
 }
 
 function h$base_st_mode(stat, stat_off) {
-    return stat.i3[(stat_off>>2)+3];
+    return h$base_return_field(stat, stat_off, OFFSET_STAT_ST_MODE, SIZEOF_STAT_ST_MODE);
 }
 
 function h$base_st_dev(stat, stat_off) {
-    return stat.i3[(stat_off>>2)+0];
+    return h$base_return_field(stat, stat_off, OFFSET_STAT_ST_DEV, SIZEOF_STAT_ST_DEV);
 }
 
 function h$base_st_ino(stat, stat_off) {
-    RETURN_UBX_TUP2(stat.i3[(stat_off>>2)+26], stat.i3[(stat_off>>2)+27]);
+    return h$base_return_field(stat, stat_off, OFFSET_STAT_ST_INO, SIZEOF_STAT_ST_INO);
 }
 
 /** @const */ var h$base_echo            = 1;


=====================================
libraries/bytestring
=====================================
@@ -1 +1 @@
-Subproject commit 9cab76dc861f651c3940e873ce921d9e09733cc8
+Subproject commit 602fd2f3470f180d64cb8baadf63e94baec66b60


=====================================
m4/emsdk_version.m4
=====================================
@@ -0,0 +1,42 @@
+dnl EMSDK_VERSION()
+dnl ---------------
+dnl
+dnl Check the version of emsdk, if we're building the JavaScript backend
+dnl (the test is skipped if the target is not JavaScript)
+dnl
+dnl If you change the version requirements, please also update:
+dnl https://gitlab.haskell.org/ghc/ghc/wikis/building/preparation/tools
+dnl
+dnl $1 = minimum version (inclusive), or "" for no minimum
+dnl $2 = maximum version (not inclusive), or "" for no maximum
+dnl $3 = exact version required, or "" for no exact version (used for bindist)
+
+AC_DEFUN([EMSDK_VERSION],
+[
+    if test "$TargetArch_CPP" = "javascript"
+    then
+        AC_MSG_CHECKING(emsdk version)
+        EmsdkVersion=`$CC -v 2>&1 | sed -n -e 's/^emcc[[^0-9]]*\([[0-9.]]*\).*/\1/gp'`
+        if test "$EmsdkVersion" = ""
+        then
+            AC_MSG_ERROR([could not determine emsdk version. Perhaps CC is not emcc?])
+        else
+            AC_MSG_RESULT($EmsdkVersion)
+            if test "$1" != ""
+            then
+                FP_COMPARE_VERSIONS([$EmsdkVersion],[-lt],[$1],
+                   [AC_MSG_ERROR([emsdk version $1 or later is required to compile the GHC JavaScript backend.])])[]
+            fi
+            if test "$2" != ""
+            then
+                FP_COMPARE_VERSIONS([$EmsdkVersion],[-ge],[$2],
+                   [AC_MSG_ERROR([emsdk version earlier than $2 is required to compile the GHC JavaScript backend.])])[]
+            fi
+            if test "$3" != ""
+            then
+                FP_COMPARE_VERSIONS([$EmsdkVersion],[-ne],[$3],
+                   [AC_MSG_ERROR([emsdk version $3 is required for this build of GHC])])[]
+            fi
+        fi
+    fi
+])


=====================================
nofib
=====================================
@@ -1 +1 @@
-Subproject commit 2cee92861c43ac74154bbd155a83f9f4ad0b9f2f
+Subproject commit 274cc3f7479431e3a52c78840b3daee887e0414f


=====================================
rts/Updates.h
=====================================
@@ -12,6 +12,347 @@
 #include "BeginPrivate.h"
 #endif
 
+/* Note [Thunks, blackholes, and indirections]
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * Consider the following STG binding:
+ *
+ *     thnk = {fv_0, fv_1} \u [] g x y;
+ *
+ * This binding is a updatable thunk carrying free variables `fv_0` and `fv_1`.
+ * Over its lifetime, this closure may transition through three states:
+ *
+ *  1. it starts life as a thunk, which carries with it free variables
+ *  2. if a thread enters it, it may turn into a blackhole
+ *  3. if evaluation finishes, it will be "updated", turning it into an
+ *     indirection pointing to the result of the evaluation
+ *
+ * On the heap, state (1) is represented as a closure with the following layout
+ * (embodied in the C runtime by the `StgThunk` struct):
+ *
+ *      thnk
+ *     ┌───────────────────────┐  ╮
+ *     │ blah_info             │  │
+ *     ├────────────┬──────────┤  │ StgThunkHeader
+ *     │ indirectee │ NULL     │  │
+ *     ├────────────┼──────────┤  ╯
+ *     │ payload[0] │ fv_0     │
+ *     ├────────────┼──────────┤
+ *     │ payload[1] │ fv_1     │
+ *     └────────────┴──────────┘
+ *
+ * Here `blah_info` is a pointer to the thunk's info table, which will be of
+ * type THUNK. The `indirectee` field (also known as the "SMP header") is
+ * initially NULL and is unused while the closure remains a thunk. However, as
+ * we will see, it will eventually point to the result of the thunk's
+ * evaluation.
+ *
+ *
+ * Entry
+ * -----
+ * As usual, to enter `thnk` (step (2) of the lifetime) a mutator thread
+ * `tso_0` will jump to its entry code (which is recorded in or next to its
+ * info table, depending upon whether tables-next-to-code is enabled). This
+ * entry code will push an "update frame" (namely, either `stg_upd_frame` or
+ * `stg_bh_upd_frame`) to `tso_0`'s stack and begin the evaluation of the
+ * thunk's RHS.
+ *
+ * However, before commencing evaluation, the entry code may also mark the
+ * thunk as being under evaluation; this process is known a "blackholing".
+ * Whether this happens depends upon which of GHC's two blackholing strategies
+ * which was selected during the compilation of the defining module. We will
+ * discuss the simpler "eager blackholing" case first and later introduce the
+ * more-efficient "lazy blackholing" strategy.
+ *
+ *
+ * Eager blackholing
+ * -----------------
+ * Under the eager blackholing strategy (which is enabled via the
+ * `-feager-blackholing` flag), a thunk's entry code (generated by
+ * `GHC.StgToCmm.Bind.emitBlackHoleCode`) will immediately turn the thunk into
+ * a blackhole, indicating that the thunk is under evaluation. Additionally,
+ * the indirectee field will be updated to point to the thread performing the
+ * evaluation, `tso_0`. Since we know statically that the thunk is now a
+ * `BLACKHOLE`, the thunk entry code will push an `stg_bh_upd_frame` to the
+ * stack in this case (in contrast to the lazy strategy, as we will see later).
+ *
+ * After this, `thnk` will look like,
+ *
+ *      thnk
+ *     ┌───────────────────────┐
+ *     │ EAGER_BLACKHOLE_info  │
+ *     ├────────────┬──────────┤           tso_0
+ *     │ indirectee │ tso_0    │─────────►┌──────┐
+ *     ├────────────┼──────────┤          │ ...  │
+ *     │ payload[0] │ fv_0     │          └──────┘
+ *     ├────────────┼──────────┤
+ *     │ payload[1] │ fv_1     │
+ *     └────────────┴──────────┘
+ *
+ * Note that blackholing in this way does not guarantee mutual exclusion: two
+ * threads may indeed race to enter `thnk`. This will result in both threads
+ * performing evaluation and, in some cases, the blackhole being updated
+ * multiple times.
+ *
+ *
+ * Updating a thunk
+ * ----------------
+ * When `tso_0` finishes the evaluation of `thnk`, it will return to the entry
+ * code of the update frame pushed when the thunk was entered (e.g.
+ * `stg_bh_upd_frame`). This code first checks whether the blackhole
+ * has already been updated by another thread; if it has then `tso_0` will
+ * throw out its result and reuse that which the earlier thread recorded in the
+ * blackhole's `indirectee` field.
+ *
+ * If the blackhole has not yet been updated then `tso_0` will:
+ *
+ *  2A. set `thnk`'s `indirectee` field to point to the result of its
+ *     evaluation, and
+ *  2B. set its info table to `BLACKHOLE_info`
+ *
+ * N.B. You might notice that step (2B) here appears to be redundant as we
+ * already set the info table pointer to `EAGER_BLACKHOLE_info` above. However,
+ * as we will see below, this may not be the case when lazy blackholing is in
+ * use.
+ *
+ * After these updates, we will have the following:
+ *
+ *      thnk
+ *     ┌───────────────────────┐
+ *     │ BLACKHOLE_info        │
+ *     ├────────────┬──────────┤           result
+ *     │ indirectee │ result   │─────────►┌────────┐
+ *     ├────────────┼──────────┤          │ ...    │
+ *     │ payload[0] │ fv_0     │          └────────┘
+ *     ├────────────┼──────────┤
+ *     │ payload[1] │ fv_1     │
+ *     └────────────┴──────────┘
+ *
+ * In addition, the code will check the blocking queues that were added to the
+ * blackhole (recorded in the `indirectee` field, as we will see below) and
+ * wake them up (see `Threads.c:updateThunk`).
+ *
+ * Note that we are using `BLACKHOLE_info` to represent two distinct states of
+ * a thunk:
+ *
+ *  - if the indirectee points to a `TSO` or `BLOCKING_QUEUE`, then the
+ *    `BLACKHOLE` represents a thunk currently being evaluated
+ *
+ *  - otherwise `BLACKHOLE` is merely representing an evaluated thunk and
+ *    serves as an indirection to the final result.
+ *
+ * This overloading may seem odd given that we also have `stg_IND_info`, which
+ * also represents an indirection.  However, the overloading serves a purpose:
+ * it means that safely updating a blackhole (step (3) of the lifetime above)
+ * requires only a single store (namely the store to the `indirectee` field).
+ *
+ * If we were to instead use `stg_IND` to represent the updated thunk, we would
+ * require two stores and consequently have an awkward period where the info
+ * table and indirectee fields are inconsistent:
+ *
+ *  - if we were to update the info table first, there would be a period where
+ *    the `indirectee` field pointed to the TSO which did the evaluation and
+ *    not the result as one would expect.
+ *
+ *  - if we were to update the indirectee first, there would be a period where
+ *    the closure is still a `BLACKHOLE_info` yet the indirectee points to the
+ *    evaluation result.
+ *
+ * For this reason, it is simpler to use `BLACKHOLE` to represent both states
+ * (2) and (3), distinguishing them using the identity of the indirectee. The
+ * uses of `stg_IND` are few and will be discussed below.
+ *
+ *
+ * Lazy blackholing
+ * ----------------
+ * While the strict blackholing strategy described above is simple and is
+ * faithful to the semantics of the STG machine, it is fairly costly on modern
+ * hardware. Specifically, thunk entry can be extremely common and in a
+ * parallel program blackholing may induce considerable pressure on the
+ * machine's memory subsystem.
+ *
+ * To mitigate this, GHC uses a lazy blackholing strategy by default. Here we
+ * take advantage of the fact that redundant evaluation of a thunk is
+ * acceptable, and defer blackholing until the thread returns to the scheduler.
+ * This is an optimisation, because we will often finish evaluation *before*
+ * yielding; in this case we avoid incurring the memory writes necessary to
+ * blackhole the thunk (step (2)), and rather update the thunk straight to an
+ * indirection.
+ *
+ * When entering a thunk compiled with lazy blackholing, we push an
+ * `stg_upd_frame` (not `stg_upd_bh_frame`) frame to the stack and do not
+ * modify the thunk closure itself.
+ *
+ * If the thread yields before finishing evaluation, the thunk will be turned
+ * into a `BLACKHOLE` in `ThreadPaused.c:threadPaused`. This function traverses
+ * the stack of the yielding thread looking for update frames; when such a
+ * frame is encountered, it checks the info table of the updatee and:
+ *
+ *  - if it is `BLACKHOLE`, then the thunk has already been claimed for evaluation
+ *    by another thread, and the yielding thread is instead added to the
+ *    `BLACKHOLE`'s blocking queue (see Note [suspend duplicate work] in
+ *    `ThreadPaused.c`).
+ *
+ *  - if not, then it blackholes the thunk as done in eager blackholing (but
+ *    using the `BLACKHOLE_info` info table instead of `EAGER_BLACKHOLE_info`).
+ *
+ * Update frames processed in this manner are rewritten to become
+ * `stg_marked_upd_frame`s. The stack traversal continues until a
+ * `stg_marked_upd_frame_info` frame is encountered, at which point we know
+ * that all subsequent frames have already been processed in a previous yield.
+ *
+ * The entry code of `stg_upd_frame` is considerably simpler than that of
+ * `stg_bh_upd_frame` since we know that the thunk has not accumulated any
+ * `BLOCKING_QUEUE`s in need of waking (since it was never blackhole'd). This
+ * is itself a small optimisation for the common case of uncontended thunk
+ * update. By contrast, the entry code of `stg_marked_upd_frame` is identical
+ * to that of `stg_bh_upd_frame` and must deal with waking of blocked threads.
+ *
+ * See `Note [suspend duplicate work]` in `ThreadPaused.c` for a subtle case
+ * involving the interaction between lazy and eager blackholing.
+ *
+ * See `Note [upd-black-hole]` in `Scav.c` for another subtle case.
+ *
+ *
+ * Blocking on a blackhole'd thunk
+ * -------------------------------
+ * If another thread `tso_1` enters `thnk` while it is blackholed by `tso_0`,
+ * the entry code of `BLACKHOLE` will allocate a `MSG_BLACKHOLE` object
+ * `msg_bh_0`. This message will be sent to the capability where the thread
+ * owning the thunk resides (see `Messages.c:messageBlackHole`). This
+ * capability will allocate a `BLOCKING_QUEUE` object `bq_0` recording the fact
+ * that `tso_1` is waiting for the result of `thnk`'s evaluation, and link it to
+ * `thnk` as follows:
+ *
+ *         thnk
+ *     ┌─►┌───────────────────────┐
+ *     │  │ EAGER_BLACKHOLE_info  │
+ *     │  ├────────────┬──────────┤
+ *     │  │ indirectee │ bq_0     ├──────┐
+ *     │  ├────────────┼──────────┤      │
+ *     │  │ payload[0] │ fv_0     │      │
+ *     │  ├────────────┼──────────┤      │
+ *     │  │ payload[1] │ fv_1     │      │
+ *     │  └────────────┴──────────┘      │
+ *     │                                 │       msg_bh_0
+ *     │     ┌───────────────────────────┘  ┌──►┌───────────────────────────┐
+ *     │     │                              │   │ MSG_BLACKHOLE_info        │
+ *     │     │                              │   ├───────────┬───────────────┤
+ *     │     │   bq_0                       │   │ link      │ END_TSO_QUEUE │
+ *     │     └─►┌──────────────────────┐    │   ├───────────┼───────────────┤
+ *     │        │ BLOCKING_QUEUE_info  │    │   │ result    │ NULL          │
+ *     │        ├───────────┬──────────┤    │   ├───────────┼───────────────┤      tso_1
+ *     │        │ link      │ NULL     │    │   │ tso       │ tso_1         ├────►┌──────┐
+ *     │        ├───────────┼──────────┤    │   └───────────┴───────────────┘     │ ...  │
+ *     │        │ queue     │ msg_bh_0 ├────┘                                     └──────┘
+ *     │        ├───────────┼──────────┤         tso_0
+ *     │        │ owner     │ tso_0    ├───────►┌──────┐
+ *     │        ├───────────┼──────────┤        │ ...  │
+ *     │        │ bh        │ thnk     ├────┐   └──────┘
+ *     │        └───────────┴──────────┘    │
+ *     │                                    │
+ *     └────────────────────────────────────┘
+ *
+ * Additionally, the `BLOCKING_QUEUE` is added to the `bq` list of the owning
+ * TSO, which collects all blocking queue objects which are blocked on thunks
+ * owned by the thread.
+ *
+ * In addition to this book-keeping, the `MSG_BLACKHOLE` message results in
+ * `tso_0` being promoted in its capability's run queue in the hope that
+ * `tso_1` and other blocked threads may be unblocked more quickly.
+ *
+ *
+ * Exception handling
+ * ------------------
+ * When an exception is thrown to a thread which is evaluating a thunk, it is
+ * important that we put things back into a state in which evaluation can
+ * be resumed by another thread. This is done by
+ * `RaiseAsync.c:raiseAsync` which walks the stack looking for update
+ * frames and rewrites the updatees into indirections pointing to an
+ * `AP_STACK` closure recording the aborted execution state.
+ * See `RaiseAsync.c:raiseAsync` for details.
+ *
+ *
+ * CAFs
+ * ----
+ * Top-level thunks (CAFs) reuse much of this machinery. The only differences
+ * are:
+ *
+ *  - CAF entry ensures mutual exclusion (see `Note [atomic CAF entry]`
+ *    in `Storage.c` for why)
+ *
+ *  - we have a distinct blackhole type, `stg_CAF_BLACKHOLE_info`; it is not
+ *    clear that maintaining this distinction from `stg_EAGER_BLACKHOLE_info`
+ *    is strictly necessary.
+ *
+ * See `Note [CAF management]` in `Storage.c` .
+ *
+ *
+ * Memory ordering
+ * ---------------
+ * The memory orderings necessary for safe concurrent thunk evaluation
+ * are rather subtle and described in Note [Heap memory barriers] in `SMP.h`.
+ *
+ *
+ * The uses of `stg_IND`
+ * ---------------------
+ * As noted above, `stg_IND_info` is not used for thunk evaluation. Instead, it
+ * merely serves as a general-purpose indirection in a few miscellaneous cases:
+ *
+ *  * it is used to "short-out" `BLOCKING_QUEUE`s and `MVAR_TSO_QUEUES` that have
+ *    already been woken-up. See Note [BLACKHOLE pointing to IND] in `Evac.c`.
+ *
+ *  * It is used to perform indirection of selector thunks (see
+ *    `Evac.c:unchain_thunk_selectors`).
+ *
+ *
+ * Indirection shortcutting
+ * ------------------------
+ * Note that the garbage collector can "shortcut" both `IND` and
+ * `BLACKHOLE` indirections. That is, the heap:
+ *
+ *  ref
+ * ┌──────────┐          evald_thunk
+ * │          ├────────►┌───────────────┬──────┐
+ * └──────────┘         │ stg_IND_info  │      │
+ *                      ├───────────────┼──────┤          x
+ *                      │ indirectee    │      ├────────►┌──────────────┐
+ *                      └───────────────┴──────┘         │ ...          │
+ *                                                       │              │
+ *                                                       └──────────────┘
+ * Can be rewritten to:
+ *
+ *  ref            ┌────────────────────────────────────┐
+ * ┌──────────┐    │     evald_thunk                    │
+ * │          ├────┘    ┌───────────────┬──────┐        │
+ * └──────────┘         │ stg_IND_info  │      │        │
+ *                      ├───────────────┼──────┤        ▼ x
+ *                      │ indirectee    │      ├────────►┌──────────────┐
+ *                      └───────────────┴──────┘         │ ...          │
+ *                                                       │              │
+ *                                                       └──────────────┘
+ *
+ *
+ * Selector optimisation
+ * ---------------------
+ * In addition to shortcutting indirections, the garbage collector can do a
+ * limited form of evaluation known as the "selector optimisation"
+ * [Wadler1987]. Specifically, the GC knows to rewrite a certain class of thunk
+ * (so-called "selector thunks", which are applications of selector functions
+ * like `fst`) into their result. For instance, given
+ *
+ *     x = (a,b)
+ *     y = fst x
+ *
+ * the GC can rewrite `y` into an indirection to `a`. References to `y` can
+ * then be further shortcutted via indirection shortcutting as described above.
+ *
+ *
+ * [Wadler1987]:
+ *   Wadler, P. (1987), Fixing some space leaks with a garbage collector.
+ *   Softw: Pract. Exper., 17: 595-608. https://doi.org/10.1002/spe.4380170904
+ */
+
+
 /* -----------------------------------------------------------------------------
    Updates
    -------------------------------------------------------------------------- */


=====================================
rts/js/arith.js
=====================================
@@ -394,9 +394,9 @@ function h$decodeFloatInt(d) {
             h$convertFloat[0] = d*8388608; // put d in the normal range (~ shift left 23 bits)
             i = h$convertInt[0];
             s = (i&8388607) | 8388608;
-            e = ((i >> 23) & 0xff) - 173; // take into account normalization above (150+23)
-            TRACE_ARITH("decodeFloatInt s: " + (sgn * s) +  " e: " + e)
-            RETURN_UBX_TUP2(sgn*s, e)
+            exp = ((i >> 23) & 0xff) - 173; // take into account normalization above (150+23)
+            TRACE_ARITH("decodeFloatInt s: " + (sgn * s) +  " e: " + exp)
+            RETURN_UBX_TUP2(sgn*s, exp)
         }
     } else {
       TRACE_ARITH("decodeFloatInt s: " + (sgn * (s|8388608)) +  " e: " + (exp-150))


=====================================
rts/js/environment.js
=====================================
@@ -166,7 +166,7 @@ function h$getProgArgv(argc_v,argc_off,argv_v,argv_off) {
 }
 
 function h$setProgArgv(n, ptr_d, ptr_o) {
-  args = [];
+  var args = [];
   for(var i=0;i<n;i++) {
     var off = ptr_o+4*i;
     GET_ADDR(ptr_d,off,p,o);


=====================================
rts/js/thread.js
=====================================
@@ -138,6 +138,7 @@ function h$getThreadLabel(t) {
 
 function h$listThreads() {
   var r = h$newArray(0,null);
+  var t;
 
   if (h$currentThread) r.push(h$currentThread);
 


=====================================
testsuite/tests/ghci/scripts/T9881.stdout
=====================================
@@ -19,19 +19,19 @@ instance Ord Data.ByteString.Lazy.ByteString
 
 type Data.ByteString.ByteString :: *
 data Data.ByteString.ByteString
-  = bytestring-0.11.4.0:Data.ByteString.Internal.Type.BS {-# UNPACK #-}(GHC.ForeignPtr.ForeignPtr
+  = bytestring-0.11.5.1:Data.ByteString.Internal.Type.BS {-# UNPACK #-}(GHC.ForeignPtr.ForeignPtr
                                                                           GHC.Word.Word8)
                                                          {-# UNPACK #-}Int
-  	-- Defined in ‘bytestring-0.11.4.0:Data.ByteString.Internal.Type’
+  	-- Defined in ‘bytestring-0.11.5.1:Data.ByteString.Internal.Type’
 instance Monoid Data.ByteString.ByteString
-  -- Defined in ‘bytestring-0.11.4.0:Data.ByteString.Internal.Type’
+  -- Defined in ‘bytestring-0.11.5.1:Data.ByteString.Internal.Type’
 instance Read Data.ByteString.ByteString
-  -- Defined in ‘bytestring-0.11.4.0:Data.ByteString.Internal.Type’
+  -- Defined in ‘bytestring-0.11.5.1:Data.ByteString.Internal.Type’
 instance Semigroup Data.ByteString.ByteString
-  -- Defined in ‘bytestring-0.11.4.0:Data.ByteString.Internal.Type’
+  -- Defined in ‘bytestring-0.11.5.1:Data.ByteString.Internal.Type’
 instance Show Data.ByteString.ByteString
-  -- Defined in ‘bytestring-0.11.4.0:Data.ByteString.Internal.Type’
+  -- Defined in ‘bytestring-0.11.5.1:Data.ByteString.Internal.Type’
 instance Eq Data.ByteString.ByteString
-  -- Defined in ‘bytestring-0.11.4.0:Data.ByteString.Internal.Type’
+  -- Defined in ‘bytestring-0.11.5.1:Data.ByteString.Internal.Type’
 instance Ord Data.ByteString.ByteString
-  -- Defined in ‘bytestring-0.11.4.0:Data.ByteString.Internal.Type’
+  -- Defined in ‘bytestring-0.11.5.1:Data.ByteString.Internal.Type’


=====================================
testsuite/tests/ghci/scripts/ghci025.stdout
=====================================
@@ -54,7 +54,7 @@ Prelude.length :: Data.Foldable.Foldable t => t a -> GHC.Types.Int
 type T.Integer :: *
 data T.Integer = ...
 T.length ::
-  bytestring-0.11.4.0:Data.ByteString.Internal.Type.ByteString
+  bytestring-0.11.5.1:Data.ByteString.Internal.Type.ByteString
   -> GHC.Types.Int
 :browse! T
 -- defined locally


=====================================
testsuite/tests/javascript/js-callback03.hs
=====================================
@@ -10,7 +10,7 @@ foreign import javascript "((x) => { globalF(x); })"
 foreign import javascript "((x,y) => { return x + y })"
   js_plus :: JSVal -> JSVal -> IO JSVal
 
-foreign import javascript "((g) => { globalG = g; })"
+foreign import javascript "((g) => { globalThis.globalG = g; })"
   setG :: Callback (JSVal -> JSVal -> IO JSVal) -> IO ()
 
 foreign import javascript "((x,y) => { return globalG(x,y); })"


=====================================
testsuite/tests/programs/seward-space-leak/test.T
=====================================
@@ -1,5 +1,4 @@
 test('seward-space-leak', [extra_files(['Main.lhs'])
                           , when(fast(), skip)
-                          , js_broken(22352)
                           ], multimod_compile_and_run,
      ['Main', ''])



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/515962908307547e47fc9c94f7f776c6e8ecd259...9574d51be6f7a0b6e4d540c13fddf48ae0ac1d07

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/515962908307547e47fc9c94f7f776c6e8ecd259...9574d51be6f7a0b6e4d540c13fddf48ae0ac1d07
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230807/857f294d/attachment-0001.html>


More information about the ghc-commits mailing list