Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
-
c9b8465c
by Cheng Shao at 2025-10-20T10:16:00-04:00
-
97a92a6d
by Julian Ospald at 2025-10-20T12:50:34-04:00
-
36ab7344
by Sven Tennie at 2025-10-20T12:50:34-04:00
-
c4e6e0df
by Sven Tennie at 2025-10-20T12:50:34-04:00
8 changed files:
- compiler/GHC/StgToJS/Linker/Linker.hs
- hadrian/src/Rules/Gmp.hs
- hadrian/src/Rules/Libffi.hs
- hadrian/src/Settings/Builders/Cabal.hs
- hadrian/src/Settings/Builders/Common.hs
- hadrian/src/Settings/Builders/DeriveConstants.hs
- hadrian/src/Settings/Builders/Hsc2Hs.hs
- utils/jsffi/dyld.mjs
Changes:
| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | {-# LANGUAGE TupleSections #-}
|
| 3 | 3 | {-# LANGUAGE LambdaCase #-}
|
| 4 | 4 | {-# LANGUAGE BlockArguments #-}
|
| 5 | +{-# LANGUAGE MultiWayIf #-}
|
|
| 5 | 6 | |
| 6 | 7 | -----------------------------------------------------------------------------
|
| 7 | 8 | -- |
|
| ... | ... | @@ -666,12 +667,19 @@ renderLinkerStats s = |
| 666 | 667 | |
| 667 | 668 | |
| 668 | 669 | getPackageArchives :: StgToJSConfig -> UnitEnv -> [UnitId] -> IO [FilePath]
|
| 669 | -getPackageArchives cfg unit_env units =
|
|
| 670 | - filterM doesFileExist [ ST.unpack p </> "lib" ++ ST.unpack l ++ profSuff <.> "a"
|
|
| 671 | - | u <- units
|
|
| 672 | - , p <- getInstalledPackageLibDirs ue_state u
|
|
| 673 | - , l <- getInstalledPackageHsLibs ue_state u
|
|
| 674 | - ]
|
|
| 670 | +getPackageArchives cfg unit_env units = do
|
|
| 671 | + fmap concat $ forM units $ \u -> do
|
|
| 672 | + let archives = [ ST.unpack p </> "lib" ++ ST.unpack l ++ profSuff <.> "a"
|
|
| 673 | + | p <- getInstalledPackageLibDirs ue_state u
|
|
| 674 | + , l <- getInstalledPackageHsLibs ue_state u
|
|
| 675 | + ]
|
|
| 676 | + foundArchives <- filterM doesFileExist archives
|
|
| 677 | + if | not (null archives)
|
|
| 678 | + , null foundArchives
|
|
| 679 | + -> do
|
|
| 680 | + throwGhcExceptionIO (InstallationError $ "Could not find any library archives for unit-id: " <> (renderWithContext (csContext cfg) $ ppr u))
|
|
| 681 | + | otherwise
|
|
| 682 | + -> pure foundArchives
|
|
| 675 | 683 | where
|
| 676 | 684 | ue_state = ue_homeUnitState unit_env
|
| 677 | 685 |
| ... | ... | @@ -11,7 +11,7 @@ import Target |
| 11 | 11 | import Utilities
|
| 12 | 12 | import Hadrian.BuildPath
|
| 13 | 13 | import Hadrian.Expression
|
| 14 | -import Settings.Builders.Common (cArgs, getStagedCCFlags)
|
|
| 14 | +import Settings.Builders.Common (getStagedCCFlags)
|
|
| 15 | 15 | |
| 16 | 16 | -- | Build in-tree GMP library objects (if GmpInTree flag is set) and return
|
| 17 | 17 | -- their paths.
|
| ... | ... | @@ -125,8 +125,7 @@ gmpRules = do |
| 125 | 125 | cFlags <-
|
| 126 | 126 | interpretInContext ctx $
|
| 127 | 127 | mconcat
|
| 128 | - [ cArgs
|
|
| 129 | - , getStagedCCFlags
|
|
| 128 | + [ getStagedCCFlags
|
|
| 130 | 129 | -- gmp symbols are only used by bignum logic in
|
| 131 | 130 | -- ghc-internal and shouldn't be exported by the
|
| 132 | 131 | -- ghc-internal shared library.
|
| ... | ... | @@ -130,17 +130,14 @@ fixLibffiMakefile top = |
| 130 | 130 | configureEnvironment :: Stage -> Action [CmdOption]
|
| 131 | 131 | configureEnvironment stage = do
|
| 132 | 132 | context <- libffiContext stage
|
| 133 | - cFlags <- interpretInContext context $ mconcat
|
|
| 134 | - [ cArgs
|
|
| 135 | - , getStagedCCFlags ]
|
|
| 136 | - ldFlags <- interpretInContext context ldArgs
|
|
| 133 | + cFlags <- interpretInContext context getStagedCCFlags
|
|
| 137 | 134 | sequence [ builderEnvironment "CC" $ Cc CompileC stage
|
| 138 | 135 | , builderEnvironment "CXX" $ Cc CompileC stage
|
| 139 | - , builderEnvironment "AR" (Ar Unpack stage)
|
|
| 136 | + , builderEnvironment "AR" $ Ar Unpack stage
|
|
| 140 | 137 | , builderEnvironment "NM" Nm
|
| 141 | 138 | , builderEnvironment "RANLIB" Ranlib
|
| 142 | 139 | , return . AddEnv "CFLAGS" $ unwords cFlags ++ " -w"
|
| 143 | - , return . AddEnv "LDFLAGS" $ unwords ldFlags ++ " -w" ]
|
|
| 140 | + , return . AddEnv "LDFLAGS" $ "-w" ]
|
|
| 144 | 141 | |
| 145 | 142 | -- Need the libffi archive and `trackAllow` all files in the build directory.
|
| 146 | 143 | -- See [Libffi indicating inputs].
|
| ... | ... | @@ -188,18 +188,16 @@ configureArgs cFlags' ldFlags' = do |
| 188 | 188 | values <- unwords <$> expr
|
| 189 | 189 | not (null values) ?
|
| 190 | 190 | arg ("--configure-option=" ++ key ++ "=" ++ values)
|
| 191 | - cFlags = mconcat [ remove ["-Werror"] cArgs
|
|
| 192 | - , getStagedCCFlags
|
|
| 191 | + cFlags = mconcat [ getStagedCCFlags
|
|
| 193 | 192 | -- See https://github.com/snowleopard/hadrian/issues/523
|
| 194 | 193 | , arg $ "-iquote"
|
| 195 | 194 | |
| 196 | 195 | , arg $ top -/- pkgPath pkg
|
| 197 | 196 | , cFlags'
|
| 198 | 197 | ]
|
| 199 | - ldFlags = ldArgs <> ldFlags'
|
|
| 200 | 198 | mconcat
|
| 201 | 199 | [ conf "CFLAGS" cFlags
|
| 202 | - , conf "LDFLAGS" ldFlags
|
|
| 200 | + , conf "LDFLAGS" ldFlags'
|
|
| 203 | 201 | , conf "--with-iconv-includes" $ arg =<< getSetting IconvIncludeDir
|
| 204 | 202 | , conf "--with-iconv-libraries" $ arg =<< getSetting IconvLibDir
|
| 205 | 203 | , conf "--with-gmp-includes" $ arg =<< getSetting GmpIncludeDir
|
| ... | ... | @@ -5,7 +5,7 @@ module Settings.Builders.Common ( |
| 5 | 5 | module Oracles.Setting,
|
| 6 | 6 | module Settings,
|
| 7 | 7 | module UserSettings,
|
| 8 | - cIncludeArgs, ldArgs, cArgs, cppArgs, cWarnings,
|
|
| 8 | + cIncludeArgs, cWarnings,
|
|
| 9 | 9 | packageDatabaseArgs, bootPackageDatabaseArgs,
|
| 10 | 10 | getStagedCCFlags, wayCcArgs
|
| 11 | 11 | ) where
|
| ... | ... | @@ -38,15 +38,6 @@ cIncludeArgs = do |
| 38 | 38 | , pure [ "-I" ++ pkgPath pkg -/- dir | dir <- incDirs ]
|
| 39 | 39 | , pure [ "-I" ++ unifyPath dir | dir <- depDirs ] ]
|
| 40 | 40 | |
| 41 | -ldArgs :: Args
|
|
| 42 | -ldArgs = mempty
|
|
| 43 | - |
|
| 44 | -cArgs :: Args
|
|
| 45 | -cArgs = mempty
|
|
| 46 | - |
|
| 47 | -cppArgs :: Args
|
|
| 48 | -cppArgs = mempty
|
|
| 49 | - |
|
| 50 | 41 | -- TODO: should be in a different file
|
| 51 | 42 | cWarnings :: Args
|
| 52 | 43 | cWarnings = mconcat
|
| ... | ... | @@ -40,8 +40,7 @@ includeCcArgs :: Args |
| 40 | 40 | includeCcArgs = do
|
| 41 | 41 | stage <- getStage
|
| 42 | 42 | rtsPath <- expr $ rtsBuildPath stage
|
| 43 | - mconcat [ cArgs
|
|
| 44 | - , cWarnings
|
|
| 43 | + mconcat [ cWarnings
|
|
| 45 | 44 | , prgFlags . ccProgram . tgtCCompiler <$> expr (targetStage Stage1)
|
| 46 | 45 | , queryTargetTarget tgtUnregisterised ? arg "-DUSE_MINIINTERPRETER"
|
| 47 | 46 | , arg "-Irts"
|
| ... | ... | @@ -50,7 +50,7 @@ getCFlags = do |
| 50 | 50 | autogen <- expr $ autogenPath context
|
| 51 | 51 | let cabalMacros = autogen -/- "cabal_macros.h"
|
| 52 | 52 | expr $ need [cabalMacros]
|
| 53 | - mconcat [ remove ["-O"] (cArgs <> getStagedCCFlags)
|
|
| 53 | + mconcat [ remove ["-O"] getStagedCCFlags
|
|
| 54 | 54 | -- Either "-E" is not part of the configured cpp args, or we can't add those args to invocations of things like this
|
| 55 | 55 | -- ROMES:TODO: , prgFlags . cppProgram . tgtCPreprocessor <$> getStagedTargetConfig
|
| 56 | 56 | , cIncludeArgs
|
| ... | ... | @@ -64,6 +64,5 @@ getCFlags = do |
| 64 | 64 | getLFlags :: Expr [String]
|
| 65 | 65 | getLFlags =
|
| 66 | 66 | mconcat [ prgFlags . ccLinkProgram . tgtCCompilerLink <$> getStagedTarget
|
| 67 | - , ldArgs
|
|
| 68 | 67 | , getContextData ldOpts
|
| 69 | 68 | , getContextData depLdOpts ] |
| ... | ... | @@ -670,7 +670,25 @@ class DyLD { |
| 670 | 670 | |
| 671 | 671 | // Wasm memory & table
|
| 672 | 672 | #memory = new WebAssembly.Memory({ initial: 1 });
|
| 673 | + |
|
| 673 | 674 | #table = new WebAssembly.Table({ element: "anyfunc", initial: 1 });
|
| 675 | + // First free slot, might be invalid when it advances to #table.length
|
|
| 676 | + #tableFree = 1;
|
|
| 677 | + // See Note [The evil wasm table grower]
|
|
| 678 | + #tableGrowInstance = new WebAssembly.Instance(
|
|
| 679 | + new WebAssembly.Module(
|
|
| 680 | + new Uint8Array([
|
|
| 681 | + 0, 97, 115, 109, 1, 0, 0, 0, 1, 6, 1, 96, 1, 127, 1, 127, 2, 35, 1, 3,
|
|
| 682 | + 101, 110, 118, 25, 95, 95, 105, 110, 100, 105, 114, 101, 99, 116, 95,
|
|
| 683 | + 102, 117, 110, 99, 116, 105, 111, 110, 95, 116, 97, 98, 108, 101, 1,
|
|
| 684 | + 112, 0, 0, 3, 2, 1, 0, 7, 31, 1, 27, 95, 95, 103, 104, 99, 95, 119, 97,
|
|
| 685 | + 115, 109, 95, 106, 115, 102, 102, 105, 95, 116, 97, 98, 108, 101, 95,
|
|
| 686 | + 103, 114, 111, 119, 0, 0, 10, 11, 1, 9, 0, 208, 112, 32, 0, 252, 15, 0,
|
|
| 687 | + 11,
|
|
| 688 | + ])
|
|
| 689 | + ),
|
|
| 690 | + { env: { __indirect_function_table: this.#table } }
|
|
| 691 | + );
|
|
| 674 | 692 | |
| 675 | 693 | // __stack_pointer
|
| 676 | 694 | #sp = new WebAssembly.Global(
|
| ... | ... | @@ -715,6 +733,82 @@ class DyLD { |
| 715 | 733 | // Global STG registers
|
| 716 | 734 | #regs = {};
|
| 717 | 735 | |
| 736 | + // Note [The evil wasm table grower]
|
|
| 737 | + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 738 | + // We need to grow the wasm table as we load shared libraries in
|
|
| 739 | + // wasm dyld. We used to directly call the table.grow() JS API,
|
|
| 740 | + // which works as expected in Firefox/Chrome, but unfortunately,
|
|
| 741 | + // WebKit's implementation of the table.grow() JS API is broken:
|
|
| 742 | + // https://bugs.webkit.org/show_bug.cgi?id=290681, which means that
|
|
| 743 | + // the wasm dyld simply does not work in WebKit-based browsers like
|
|
| 744 | + // Safari.
|
|
| 745 | + //
|
|
| 746 | + // Now, one simple workaround would be to avoid growing the table at
|
|
| 747 | + // all: just allocate a huge table upfront (current limitation
|
|
| 748 | + // agreed by all vendors is 10000000). To avoid unnecessary space
|
|
| 749 | + // waste on non-WebKit platforms, we could additionally check
|
|
| 750 | + // navigator.userAgent against some regexes and only allocate
|
|
| 751 | + // fixed-length table when there's no blink/gecko mention. But this
|
|
| 752 | + // is fragile and gross, and it's better to stick to a uniform code
|
|
| 753 | + // path for all browsers.
|
|
| 754 | + //
|
|
| 755 | + // Fortunately, it turns out the table.grow wasm instruction work as
|
|
| 756 | + // expected in WebKit! So we can invoke a wasm function that grows
|
|
| 757 | + // the table for us. But don't open a champagne yet, where would
|
|
| 758 | + // that wasm function come from? It can't be put into RTS, or even
|
|
| 759 | + // libc.so, because loading those libraries would require growing
|
|
| 760 | + // the table in the first place! Or perhaps, reserve a table upfront
|
|
| 761 | + // that's just large enough to load RTS and then we can access that
|
|
| 762 | + // function for subsequent table grows? But then we need to
|
|
| 763 | + // experiment for a reasonable initial size, and add a magic number
|
|
| 764 | + // here, which is also fragile and gross and not future-proof!
|
|
| 765 | + //
|
|
| 766 | + // So this special wasm function needs to live in a single wasm
|
|
| 767 | + // module, which is loaded before we load anything else. The full
|
|
| 768 | + // source code for this module is:
|
|
| 769 | + //
|
|
| 770 | + // (module
|
|
| 771 | + // (type (func (param i32) (result i32)))
|
|
| 772 | + // (import "env" "__indirect_function_table" (table 0 funcref))
|
|
| 773 | + // (export "__ghc_wasm_jsffi_table_grow" (func 0))
|
|
| 774 | + // (func (type 0) (param i32) (result i32)
|
|
| 775 | + // ref.null func
|
|
| 776 | + // local.get 0
|
|
| 777 | + // table.grow 0
|
|
| 778 | + // )
|
|
| 779 | + // )
|
|
| 780 | + //
|
|
| 781 | + // This module is 103 bytes so that we can inline its blob in dyld,
|
|
| 782 | + // and use the usually discouraged synchronous
|
|
| 783 | + // WebAssembly.Instance/WebAssembly.Module constructors to load it.
|
|
| 784 | + // On non-WebKit platforms, growing tables this way would introduce
|
|
| 785 | + // a bit of extra JS/Wasm interop overhead, which can be amplified
|
|
| 786 | + // as we used to call table.grow(1, foo) for every GOT.func item.
|
|
| 787 | + // Therefore, unless we're about to exceed the hard limit of table
|
|
| 788 | + // size, we now grow the table exponentially, and use bump
|
|
| 789 | + // allocation to calculate the table index to be returned.
|
|
| 790 | + // Exponential growth is only implemented to minimize the JS/Wasm
|
|
| 791 | + // interop overhead when calling __ghc_wasm_jsffi_table_grow;
|
|
| 792 | + // V8/SpiderMonkey/WebKit already do their own exponential growth of
|
|
| 793 | + // the table's backing buffer in their table growth logic.
|
|
| 794 | + //
|
|
| 795 | + // Invariants: n >= 0; when v is non-null, n === 1
|
|
| 796 | + #tableGrow(n, v) {
|
|
| 797 | + const prev_free = this.#tableFree;
|
|
| 798 | + if (prev_free + n > this.#table.length) {
|
|
| 799 | + const min_delta = prev_free + n - this.#table.length;
|
|
| 800 | + const delta = Math.max(min_delta, this.#table.length);
|
|
| 801 | + this.#tableGrowInstance.exports.__ghc_wasm_jsffi_table_grow(
|
|
| 802 | + this.#table.length + delta <= 10000000 ? delta : min_delta
|
|
| 803 | + );
|
|
| 804 | + }
|
|
| 805 | + if (v) {
|
|
| 806 | + this.#table.set(prev_free, v);
|
|
| 807 | + }
|
|
| 808 | + this.#tableFree += n;
|
|
| 809 | + return prev_free;
|
|
| 810 | + }
|
|
| 811 | + |
|
| 718 | 812 | constructor({ args, rpc }) {
|
| 719 | 813 | this.#rpc = rpc;
|
| 720 | 814 | |
| ... | ... | @@ -878,7 +972,7 @@ class DyLD { |
| 878 | 972 | |
| 879 | 973 | // __memory_base & __table_base, different for each .so
|
| 880 | 974 | let memory_base;
|
| 881 | - let table_base = this.#table.grow(tableSize);
|
|
| 975 | + let table_base = this.#tableGrow(tableSize);
|
|
| 882 | 976 | console.assert(tableP2Align === 0);
|
| 883 | 977 | |
| 884 | 978 | // libc.so is always the first one to be ever loaded and has VIP
|
| ... | ... | @@ -982,7 +1076,7 @@ class DyLD { |
| 982 | 1076 | if (this.exportFuncs[name]) {
|
| 983 | 1077 | this.#gotFunc[name] = new WebAssembly.Global(
|
| 984 | 1078 | { value: "i32", mutable: true },
|
| 985 | - this.#table.grow(1, this.exportFuncs[name])
|
|
| 1079 | + this.#tableGrow(1, this.exportFuncs[name])
|
|
| 986 | 1080 | );
|
| 987 | 1081 | continue;
|
| 988 | 1082 | }
|
| ... | ... | @@ -1033,7 +1127,7 @@ class DyLD { |
| 1033 | 1127 | if (this.#gotFunc[k]) {
|
| 1034 | 1128 | const got = this.#gotFunc[k];
|
| 1035 | 1129 | if (got.value === DyLD.#poison) {
|
| 1036 | - const idx = this.#table.grow(1, v);
|
|
| 1130 | + const idx = this.#tableGrow(1, v);
|
|
| 1037 | 1131 | got.value = idx;
|
| 1038 | 1132 | } else {
|
| 1039 | 1133 | this.#table.set(got.value, v);
|
| ... | ... | @@ -1103,7 +1197,7 @@ class DyLD { |
| 1103 | 1197 | // Not in GOT.func yet, create the entry on demand
|
| 1104 | 1198 | if (this.exportFuncs[sym]) {
|
| 1105 | 1199 | console.assert(!this.#gotFunc[sym]);
|
| 1106 | - const addr = this.#table.grow(1, this.exportFuncs[sym]);
|
|
| 1200 | + const addr = this.#tableGrow(1, this.exportFuncs[sym]);
|
|
| 1107 | 1201 | this.#gotFunc[sym] = new WebAssembly.Global(
|
| 1108 | 1202 | { value: "i32", mutable: true },
|
| 1109 | 1203 | addr
|