diff --git a/ci.nix b/ci.nix index e07482ba5e..a674c1bbb1 100644 --- a/ci.nix +++ b/ci.nix @@ -87,6 +87,10 @@ && (__match ".*llvm" compiler-nix-name == null) && !builtins.elem compiler-nix-name ["ghc9102"]) { inherit (lib.systems.examples) ghcjs; + } // lib.optionalAttrs (nixpkgsName == "unstable" + && (__match ".*llvm" compiler-nix-name == null) + && !builtins.elem compiler-nix-name ["ghc967" "ghc984" "ghc9102"]) { + inherit (lib.systems.examples) wasi32; } // lib.optionalAttrs (nixpkgsName == "unstable" && (__match ".*llvm" compiler-nix-name == null) && ((system == "x86_64-linux" && !builtins.elem compiler-nix-name ["ghc902" "ghc928" "ghc966" "ghc967" "ghc96720250227"]) # Not sure why GHC 9.6.6 TH code now wants `log1pf` diff --git a/compiler/ghc/default.nix b/compiler/ghc/default.nix index 9d31c197cf..c1c2139c5f 100644 --- a/compiler/ghc/default.nix +++ b/compiler/ghc/default.nix @@ -21,7 +21,7 @@ let self = libffi ? null , # we don't need LLVM for x86, aarch64, or ghcjs - useLLVM ? with stdenv.targetPlatform; !(isx86 || isAarch64 || isGhcjs) + useLLVM ? with stdenv.targetPlatform; !(isx86 || isAarch64 || isGhcjs || isWasm) , # LLVM is conceptually a run-time-only dependency, but for # non-x86, we need LLVM to bootstrap later stages, so it becomes a # build-time dependency too. @@ -117,6 +117,34 @@ let INTEGER_LIBRARY = ${if enableIntegerSimple then "integer-simple" else "integer-gmp"} ''; + libffi-wasm = buildPackages.runCommand "libffi-wasm" { + nativeBuildInputs = [ + (buildPackages.haskell-nix.tool "ghc912" "libffi-wasm" { + src = buildPackages.haskell-nix.sources.libffi-wasm; + }) + targetPackages.buildPackages.llvmPackages.clang + targetPackages.buildPackages.llvmPackages.llvm + targetPackages.buildPackages.binaryen + ]; + outputs = ["dev" "out"]; + NIX_NO_SELF_RPATH = true; + } '' + mkdir cbits + cp ${buildPackages.haskell-nix.sources.libffi-wasm}/cbits/* cbits/ + libffi-wasm + wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -c cbits/ffi.c -o cbits/ffi.o + wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -c cbits/ffi_call.c -o cbits/ffi_call.o + wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -c cbits/ffi_closure.c -o cbits/ffi_closure.o + + mkdir -p $dev/include + cp cbits/*.h $dev/include + mkdir -p $out/lib + llvm-ar -r $out/lib/libffi.a cbits/*.o + + wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -fPIC -fvisibility=default -shared -Wl,--keep-section=target_features,--strip-debug cbits/*.c -o libffi.so + wasm-opt --low-memory-unused --converge --debuginfo --flatten --rereloop --gufa -O4 -Oz libffi.so -o $out/lib/libffi.so + ''; + # TODO check if this possible fix for segfaults works or not. targetLibffi = # on native platforms targetPlatform.{libffi, gmp} do not exist; thus fall back @@ -124,7 +152,9 @@ let let targetLibffi = targetPackages.libffi or libffi; in # we need to set `dontDisableStatic` for musl for libffi to work. if stdenv.targetPlatform.isMusl - then targetLibffi.overrideAttrs (_old: { dontDisableStatic = true; }) + then targetLibffi.overrideAttrs (_old: { dontDisableStatic = true; }) + else if stdenv.targetPlatform.isWasm + then libffi-wasm else targetLibffi; targetGmp = targetPackages.gmp or gmp; @@ -195,13 +225,15 @@ let # `--with` flags for libraries needed for RTS linker configureFlags = [ "--datadir=$doc/share/doc/ghc" - ] ++ lib.optionals (!targetPlatform.isGhcjs && !targetPlatform.isAndroid) ["--with-curses-includes=${targetPackages.ncurses.dev}/include" "--with-curses-libraries=${targetPackages.ncurses.out}/lib" - ] ++ lib.optionals (targetLibffi != null && !targetPlatform.isGhcjs) ["--with-system-libffi" "--with-ffi-includes=${targetLibffi.dev}/include" "--with-ffi-libraries=${targetLibffi.out}/lib" - ] ++ lib.optionals (!enableIntegerSimple && !targetPlatform.isGhcjs) [ + ] ++ lib.optionals (!targetPlatform.isGhcjs && !targetPlatform.isWasm && !targetPlatform.isAndroid) ["--with-curses-includes=${targetPackages.ncurses.dev}/include" "--with-curses-libraries=${targetPackages.ncurses.out}/lib" + ] ++ lib.optionals (targetLibffi != null && !targetPlatform.isGhcjs && !targetPlatform.isWasm) ["--with-system-libffi" "--with-ffi-includes=${targetLibffi.dev}/include" "--with-ffi-libraries=${targetLibffi.out}/lib" + ] ++ lib.optionals (targetPlatform.isWasm) [ + "--with-system-libffi" + ] ++ lib.optionals (!enableIntegerSimple && !targetPlatform.isGhcjs && !targetPlatform.isWasm) [ "--with-gmp-includes=${targetGmp.dev}/include" "--with-gmp-libraries=${targetGmp.out}/lib" ] ++ lib.optionals (targetPlatform == hostPlatform && hostPlatform.libc != "glibc" && !targetPlatform.isWindows) [ "--with-iconv-includes=${libiconv}/include" "--with-iconv-libraries=${libiconv}/lib" - ] ++ lib.optionals (targetPlatform != hostPlatform && !targetPlatform.isGhcjs) [ + ] ++ lib.optionals (targetPlatform != hostPlatform && !targetPlatform.isGhcjs && !targetPlatform.isWasm) [ "--with-iconv-includes=${targetIconv}/include" "--with-iconv-libraries=${targetIconv}/lib" ] ++ lib.optionals (targetPlatform != hostPlatform) [ "--enable-bootstrap-with-devel-snapshot" @@ -229,9 +261,9 @@ let ; # Splicer will pull out correct variations - libDeps = platform: lib.optional (enableTerminfo && !targetPlatform.isGhcjs && !targetPlatform.isAndroid) [ targetPackages.ncurses targetPackages.ncurses.dev ] + libDeps = platform: lib.optional (enableTerminfo && !targetPlatform.isGhcjs && !targetPlatform.isWasm && !targetPlatform.isAndroid) [ targetPackages.ncurses targetPackages.ncurses.dev ] ++ lib.optional (!targetPlatform.isGhcjs) targetLibffi - ++ lib.optional (!enableIntegerSimple && !targetPlatform.isGhcjs) gmp + ++ lib.optional (!enableIntegerSimple && !targetPlatform.isGhcjs && !targetPlatform.isWasm) gmp ++ lib.optional (platform.libc != "glibc" && !targetPlatform.isWindows) libiconv ++ lib.optional (enableNUMA && platform.isLinux && !platform.isAarch32 && !platform.isAndroid) numactl ++ lib.optional enableDWARF (lib.getLib elfutils); @@ -318,12 +350,12 @@ let # For build flavours and flavour transformers # see https://gitlab.haskell.org/ghc/ghc/blob/master/hadrian/doc/flavours.md hadrianArgs = "--flavour=${ - (if targetPlatform.isGhcjs then "quick" else "default") + (if targetPlatform.isGhcjs || targetPlatform.isWasm then "quick" else "default") + lib.optionalString (!enableShared) "+no_dynamic_ghc" + lib.optionalString useLLVM "+llvm" + lib.optionalString enableDWARF "+debug_info" - + lib.optionalString ((enableNativeBignum && hadrianHasNativeBignumFlavour) || targetPlatform.isGhcjs) "+native_bignum" - + lib.optionalString targetPlatform.isGhcjs "+no_profiled_libs" + + lib.optionalString ((enableNativeBignum && hadrianHasNativeBignumFlavour) || targetPlatform.isGhcjs || targetPlatform.isWasm) "+native_bignum" + + lib.optionalString (targetPlatform.isGhcjs || targetPlatform.isWasm) "+no_profiled_libs" } --docs=no-sphinx -j --verbose" # This is needed to prevent $GCC from emitting out of line atomics. # Those would then result in __aarch64_ldadd1_sync and others being referenced, which @@ -335,8 +367,12 @@ let + lib.optionalString (!hostPlatform.isAarch64 && targetPlatform.isLinux && targetPlatform.isAarch64) " '*.rts.ghc.c.opts += -optc-mno-outline-atomics'" # PIC breaks GHC annotations on windows (see test/annotations for a test case) - + lib.optionalString (enableRelocatedStaticLibs && !targetPlatform.isWindows) + + lib.optionalString (enableRelocatedStaticLibs && !targetPlatform.isWindows && !targetPlatform.isWasm) " '*.*.ghc.*.opts += -fPIC' '*.*.cc.*.opts += -fPIC'" + # C options for wasm + + lib.optionalString targetPlatform.isWasm ( + " 'stage1.*.ghc.*.opts += -optc-Wno-error=int-conversion -optc-O3 -optc-mcpu=lime1 -optc-mreference-types -optc-msimd128 -optc-mtail-call -optc-DXXH_NO_XXH3'" + + " 'stage1.*.ghc.cpp.opts += -optc-fno-exceptions'") # `-fexternal-dynamic-refs` causes `undefined reference` errors when building GHC cross compiler for windows + lib.optionalString (enableRelocatedStaticLibs && targetPlatform.isx86_64 && !targetPlatform.isWindows) " '*.*.ghc.*.opts += -fexternal-dynamic-refs'" @@ -441,9 +477,23 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec { fi mv config.sub.ghcjs config.sub '') + + lib.optionalString (targetPlatform.isWasm) '' + export CC="${targetCC}/bin/${targetCC.targetPrefix}cc" + export CXX="${targetCC}/bin/${targetCC.targetPrefix}c++" + export LD="${buildPackages.llvmPackages.lld}/bin/wasm-ld" + export AS="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}as" + export AR="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}ar" + export NM="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}nm" + export RANLIB="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}ranlib" + export READELF="${targetCC.bintools.bintools}/bin/${targetCC.bintools.targetPrefix}readelf" + export STRIP="${bintoolsFor.strip}/bin/${bintoolsFor.strip.targetPrefix}strip" + export NIX_CFLAGS_COMPILE_FOR_BUILD+=" -I${libffi.dev}/include -L${libffi.out}/lib" + export NIX_CFLAGS_COMPILE_FOR_TARGET+=" -I${targetLibffi.dev}/include -L${targetLibffi.out}/lib" + substituteInPlace compiler/GHC.hs --replace-fail "panic \"corrupted wasi-sdk installation\"" "pure \"${targetPackages.wasilibc}\"" + '' # GHC is a bit confused on its cross terminology, as these would normally be # the *host* tools. - + lib.optionalString (!targetPlatform.isGhcjs) ('' + + lib.optionalString (!targetPlatform.isGhcjs && !targetPlatform.isWasm) ('' export CC="${targetCC}/bin/${targetCC.targetPrefix}cc" export CXX="${targetCC}/bin/${targetCC.targetPrefix}c++" '' diff --git a/flake.lock b/flake.lock index 54c9133fd8..05bf5638bc 100644 --- a/flake.lock +++ b/flake.lock @@ -100,23 +100,6 @@ "type": "github" } }, - "ghc-8.6.5-iohk": { - "flake": false, - "locked": { - "lastModified": 1600920045, - "narHash": "sha256-DO6kxJz248djebZLpSzTGD6s8WRpNI9BTwUeOf5RwY8=", - "owner": "input-output-hk", - "repo": "ghc", - "rev": "95713a6ecce4551240da7c96b6176f980af75cae", - "type": "github" - }, - "original": { - "owner": "input-output-hk", - "ref": "release/8.6.5-iohk", - "repo": "ghc", - "type": "github" - } - }, "hackage": { "flake": false, "locked": { @@ -507,7 +490,6 @@ "cabal-36": "cabal-36", "cardano-shell": "cardano-shell", "flake-compat": "flake-compat", - "ghc-8.6.5-iohk": "ghc-8.6.5-iohk", "hackage": "hackage", "hackage-for-stackage": "hackage-for-stackage", "hls": "hls", diff --git a/flake.nix b/flake.nix index 12f31554a9..f632b3d718 100644 --- a/flake.nix +++ b/flake.nix @@ -50,13 +50,6 @@ url = "github:input-output-hk/cardano-shell"; flake = false; }; - "ghc-8.6.5-iohk" = { - type = "github"; - owner = "input-output-hk"; - repo = "ghc"; - ref = "release/8.6.5-iohk"; - flake = false; - }; hpc-coveralls = { url = "github:sevanspowell/hpc-coveralls"; flake = false; diff --git a/lazy-inputs/default.nix b/lazy-inputs/default.nix index 9552c84685..1fe105262f 100644 --- a/lazy-inputs/default.nix +++ b/lazy-inputs/default.nix @@ -41,6 +41,7 @@ in { inherit ((callFlake { pkgs = final; src = ./ghc9122; }).defaultNix) ghc9122; inherit ((callFlake { pkgs = final; src = ./ghc912X; }).defaultNix) ghc912X; inherit ((callFlake { pkgs = final; src = ./ghc913; }).defaultNix) ghc913; + inherit ((callFlake { pkgs = final; src = ./libffi-wasm; }).defaultNix) libffi-wasm; } // prev.haskell-nix.sources; }; } diff --git a/lazy-inputs/libffi-wasm/flake.lock b/lazy-inputs/libffi-wasm/flake.lock new file mode 100644 index 0000000000..1240847b6a --- /dev/null +++ b/lazy-inputs/libffi-wasm/flake.lock @@ -0,0 +1,29 @@ +{ + "nodes": { + "libffi-wasm": { + "flake": false, + "locked": { + "host": "gitlab.haskell.org", + "lastModified": 1741286406, + "narHash": "sha256-GxC9G+LYRHwKArwXNeU9Enq7u4arvPbFfE4kQj1cHyc=", + "owner": "haskell-wasm", + "repo": "libffi-wasm", + "rev": "e2dbdd8ded953e7a4df42c192da65ab36b9f0345", + "type": "gitlab" + }, + "original": { + "host": "gitlab.haskell.org", + "owner": "haskell-wasm", + "repo": "libffi-wasm", + "type": "gitlab" + } + }, + "root": { + "inputs": { + "libffi-wasm": "libffi-wasm" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/lazy-inputs/libffi-wasm/flake.nix b/lazy-inputs/libffi-wasm/flake.nix new file mode 100644 index 0000000000..447a162aff --- /dev/null +++ b/lazy-inputs/libffi-wasm/flake.nix @@ -0,0 +1,12 @@ +{ + description = "Lazy Input for Haskell.nix"; + + inputs = { + libffi-wasm = { + flake = false; + url = "gitlab:haskell-wasm/libffi-wasm?host=gitlab.haskell.org"; + }; + }; + + outputs = inputs: inputs; +} diff --git a/lib/call-cabal-project-to-nix.nix b/lib/call-cabal-project-to-nix.nix index 4e7b3a73d6..5c4e975b07 100644 --- a/lib/call-cabal-project-to-nix.nix +++ b/lib/call-cabal-project-to-nix.nix @@ -331,6 +331,8 @@ let then "OSMinGW32" else if pkgs.stdenv.targetPlatform.isGhcjs then "OSGhcjs" + else if pkgs.stdenv.targetPlatform.isWasi + then "OSWasi" else throw "Unknown target os ${pkgs.stdenv.targetPlatform.config}" }")' echo ',("target arch","${ @@ -346,6 +348,8 @@ let then "ArchAArch32" else if pkgs.stdenv.targetPlatform.isJavaScript then "ArchJavaScript" + else if pkgs.stdenv.targetPlatform.isWasm + then "ArchWasm32" else throw "Unknown target arch ${pkgs.stdenv.targetPlatform.config}" }")' echo ',("target platform string","${platformString pkgs.stdenv.targetPlatform}")' diff --git a/lib/host-map.nix b/lib/host-map.nix index b4a529f035..d91970d2ba 100644 --- a/lib/host-map.nix +++ b/lib/host-map.nix @@ -18,6 +18,7 @@ with stdenv.hostPlatform; { if isiOS then "Ios" else if isAndroid then "Android" else if isGhcjs then "Ghcjs" else + if isWasi then "Wasi" else if isAsterius then "Asterius" else throw "Unknown OS"; arch = if isx86 && is32bit then "I386" else diff --git a/overlays/default.nix b/overlays/default.nix index bb5a18f655..a74dca70cd 100644 --- a/overlays/default.nix +++ b/overlays/default.nix @@ -92,6 +92,7 @@ let cacheCompilerDeps = import ./cache-compiler-deps.nix; lazy-inputs = import ../lazy-inputs; rcodesign = import ./rcodesign.nix; + wasm = import ./wasm.nix; }; composeExtensions = f: g: final: prev: @@ -126,6 +127,7 @@ let cabalPkgConfig gobject-introspection hix + wasm # Restore nixpkgs haskell and haskellPackages (_: prev: { inherit (prev.haskell-nix-prev) haskell haskellPackages; }) cacheCompilerDeps diff --git a/overlays/patches/wasm/llvm/gnu-install-dirs.patch b/overlays/patches/wasm/llvm/gnu-install-dirs.patch new file mode 100644 index 0000000000..482325452c --- /dev/null +++ b/overlays/patches/wasm/llvm/gnu-install-dirs.patch @@ -0,0 +1,152 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c9ff3696e22d..bd96aab5e237 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1133,9 +1133,9 @@ if (NOT TENSORFLOW_AOT_PATH STREQUAL "") + add_subdirectory(${TENSORFLOW_AOT_PATH}/xla_aot_runtime_src + ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/tf_runtime) + install(TARGETS tf_xla_runtime EXPORT LLVMExports +- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT tf_xla_runtime) ++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT tf_xla_runtime) + install(TARGETS tf_xla_runtime EXPORT LLVMDevelopmentExports +- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT tf_xla_runtime) ++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT tf_xla_runtime) + set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS tf_xla_runtime) + # Once we add more modules, we should handle this more automatically. + if (DEFINED LLVM_OVERRIDE_MODEL_HEADER_INLINERSIZEMODEL) +diff --git a/cmake/modules/AddLLVM.cmake b/cmake/modules/AddLLVM.cmake +index baf47677b247..81954240a9bf 100644 +--- a/cmake/modules/AddLLVM.cmake ++++ b/cmake/modules/AddLLVM.cmake +@@ -974,8 +974,8 @@ macro(add_llvm_library name) + endif() + install(TARGETS ${name} + ${export_to_llvmexports} +- LIBRARY DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name} +- ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} COMPONENT ${name} ++ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name} ++ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}${LLVM_LIBDIR_SUFFIX} COMPONENT ${name} + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT ${name}) + + if (NOT LLVM_ENABLE_IDE) +@@ -2240,7 +2240,7 @@ function(llvm_install_library_symlink name dest type) + set(LLVM_LINK_OR_COPY copy) + endif() + +- set(output_dir lib${LLVM_LIBDIR_SUFFIX}) ++ set(output_dir ${CMAKE_INSTALL_FULL_LIBDIR}${LLVM_LIBDIR_SUFFIX}) + if(WIN32 AND "${type}" STREQUAL "SHARED") + set(output_dir "${CMAKE_INSTALL_BINDIR}") + endif() +@@ -2516,15 +2516,37 @@ function(llvm_setup_rpath name) + + if (APPLE) + set(_install_name_dir INSTALL_NAME_DIR "@rpath") +- set(_install_rpath "@loader_path/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) ++ set(_install_rpath ${extra_libdir}) + elseif(${CMAKE_SYSTEM_NAME} MATCHES "AIX" AND BUILD_SHARED_LIBS) + # $ORIGIN is not interpreted at link time by aix ld. + # Since BUILD_SHARED_LIBS is only recommended for use by developers, + # hardcode the rpath to build/install lib dir first in this mode. + # FIXME: update this when there is better solution. +- set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) ++ set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_FULL_LIBDIR}${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) + elseif(UNIX) +- set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) ++ # Note that we add `extra_libdir` (aka `LLVM_LIBRARY_DIR` in our case) back ++ # to `_install_rpath` here. ++ # ++ # In nixpkgs we do not build and install LLVM alongside rdeps of LLVM (i.e. ++ # clang); instead LLVM is its own package and thus lands at its own nix ++ # store path. This makes it so that the default relative rpath (`../lib/`) ++ # does not point at the LLVM shared objects. ++ # ++ # More discussion here: ++ # - https://github.com/NixOS/nixpkgs/pull/235624#discussion_r1220150329 ++ # - https://reviews.llvm.org/D146918 (16.0.5+) ++ # ++ # Note that we leave `extra_libdir` in `_build_rpath`: without FHS there is ++ # no potential that this will result in us pulling in the "wrong" LLVM. ++ # Adding this to the build rpath means we aren't forced to use ++ # `installCheckPhase` instead of `checkPhase` (i.e. binaries in the build ++ # dir, pre-install, will have the right rpath for LLVM). ++ # ++ # As noted in the differential above, an alternative solution is to have ++ # all rdeps of nixpkgs' LLVM (that use the AddLLVM.cmake machinery) set ++ # `CMAKE_INSTALL_RPATH`. ++ set(_build_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) ++ set(_install_rpath ${extra_libdir}) + if(${CMAKE_SYSTEM_NAME} MATCHES "(FreeBSD|DragonFly)") + set_property(TARGET ${name} APPEND_STRING PROPERTY + LINK_FLAGS " -Wl,-z,origin ") +@@ -2539,9 +2561,9 @@ function(llvm_setup_rpath name) + endif() + + # Enable BUILD_WITH_INSTALL_RPATH unless CMAKE_BUILD_RPATH is set. +- if("${CMAKE_BUILD_RPATH}" STREQUAL "") +- set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) +- endif() ++ #if("${CMAKE_BUILD_RPATH}" STREQUAL "") ++ # set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) ++ #endif() + + set_target_properties(${name} PROPERTIES + INSTALL_RPATH "${_install_rpath}" +diff --git a/cmake/modules/AddOCaml.cmake b/cmake/modules/AddOCaml.cmake +index 2d9116b08a52..2dd7cad4ec66 100644 +--- a/cmake/modules/AddOCaml.cmake ++++ b/cmake/modules/AddOCaml.cmake +@@ -147,9 +147,9 @@ function(add_ocaml_library name) + endforeach() + + if( APPLE ) +- set(ocaml_rpath "@executable_path/../../../lib${LLVM_LIBDIR_SUFFIX}") ++ set(ocaml_rpath ${LLVM_LIBRARY_DIR}) + elseif( UNIX ) +- set(ocaml_rpath "\\$ORIGIN/../../../lib${LLVM_LIBDIR_SUFFIX}") ++ set(ocaml_rpath ${LLVM_LIBRARY_DIR}) + endif() + list(APPEND ocaml_flags "-ldopt" "-Wl,-rpath,${ocaml_rpath}") + +diff --git a/cmake/modules/CMakeLists.txt b/cmake/modules/CMakeLists.txt +index ef4cfa3acdb5..7478e157a7c2 100644 +--- a/cmake/modules/CMakeLists.txt ++++ b/cmake/modules/CMakeLists.txt +@@ -130,7 +130,7 @@ set(LLVM_CONFIG_INCLUDE_DIRS + ) + list(REMOVE_DUPLICATES LLVM_CONFIG_INCLUDE_DIRS) + +-extend_path(LLVM_CONFIG_LIBRARY_DIR "\${LLVM_INSTALL_PREFIX}" "lib\${LLVM_LIBDIR_SUFFIX}") ++extend_path(LLVM_CONFIG_LIBRARY_DIR "\${LLVM_INSTALL_PREFIX}" "${CMAKE_INSTALL_LIBDIR}\${LLVM_LIBDIR_SUFFIX}") + set(LLVM_CONFIG_LIBRARY_DIRS + "${LLVM_CONFIG_LIBRARY_DIR}" + # FIXME: Should there be other entries here? +diff --git a/tools/llvm-config/BuildVariables.inc.in b/tools/llvm-config/BuildVariables.inc.in +index 370005cd8d7d..7e790bc52111 100644 +--- a/tools/llvm-config/BuildVariables.inc.in ++++ b/tools/llvm-config/BuildVariables.inc.in +@@ -23,6 +23,7 @@ + #define LLVM_CXXFLAGS "@LLVM_CXXFLAGS@" + #define LLVM_BUILDMODE "@LLVM_BUILDMODE@" + #define LLVM_LIBDIR_SUFFIX "@LLVM_LIBDIR_SUFFIX@" ++#define LLVM_INSTALL_LIBDIR "@CMAKE_INSTALL_LIBDIR@" + #define LLVM_INSTALL_INCLUDEDIR "@CMAKE_INSTALL_INCLUDEDIR@" + #define LLVM_INSTALL_PACKAGE_DIR "@LLVM_INSTALL_PACKAGE_DIR@" + #define LLVM_TARGETS_BUILT "@LLVM_TARGETS_BUILT@" +diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp +index d5b76b1bb6c1..1dbdb2a8f10d 100644 +--- a/tools/llvm-config/llvm-config.cpp ++++ b/tools/llvm-config/llvm-config.cpp +@@ -366,7 +366,11 @@ int main(int argc, char **argv) { + sys::fs::make_absolute(ActivePrefix, Path); + ActiveBinDir = std::string(Path); + } +- ActiveLibDir = ActivePrefix + "/lib" + LLVM_LIBDIR_SUFFIX; ++ { ++ SmallString<256> Path(LLVM_INSTALL_LIBDIR LLVM_LIBDIR_SUFFIX); ++ sys::fs::make_absolute(ActivePrefix, Path); ++ ActiveLibDir = std::string(Path); ++ } + { + SmallString<256> Path(LLVM_INSTALL_PACKAGE_DIR); + sys::fs::make_absolute(ActivePrefix, Path); diff --git a/overlays/patches/wasm/llvm/haskell-wasm-llvm-project.patch b/overlays/patches/wasm/llvm/haskell-wasm-llvm-project.patch new file mode 100644 index 0000000000..d45b5d0880 --- /dev/null +++ b/overlays/patches/wasm/llvm/haskell-wasm-llvm-project.patch @@ -0,0 +1,322 @@ +diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp +index bd25fd1a8933..b906bff5d5c7 100644 +--- a/clang/lib/Driver/ToolChains/WebAssembly.cpp ++++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp +@@ -6,6 +6,8 @@ + // + //===----------------------------------------------------------------------===// + ++#include ++ + #include "WebAssembly.h" + #include "CommonArgs.h" + #include "Gnu.h" +@@ -19,6 +21,7 @@ + #include "llvm/Option/ArgList.h" + #include "llvm/Support/FileSystem.h" + #include "llvm/Support/Path.h" ++#include "llvm/Support/Process.h" + #include "llvm/Support/VirtualFileSystem.h" + + using namespace clang::driver; +@@ -168,21 +171,12 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA, + CmdArgs.push_back("-o"); + CmdArgs.push_back(Output.getFilename()); + +- // Don't use wasm-opt by default on `wasip2` as it doesn't have support for +- // components at this time. Retain the historical default otherwise, though, +- // of running `wasm-opt` by default. +- bool WasmOptDefault = !TargetBuildsComponents(ToolChain.getTriple()); +- bool RunWasmOpt = Args.hasFlag(options::OPT_wasm_opt, +- options::OPT_no_wasm_opt, WasmOptDefault); +- + // If wasm-opt is enabled and optimizations are happening look for the + // `wasm-opt` program. If it's not found auto-disable it. + std::string WasmOptPath; +- if (RunWasmOpt && Args.getLastArg(options::OPT_O_Group)) { +- WasmOptPath = ToolChain.GetProgramPath("wasm-opt"); +- if (WasmOptPath == "wasm-opt") { +- WasmOptPath = {}; +- } ++ WasmOptPath = ToolChain.GetProgramPath("wasm-opt"); ++ if (WasmOptPath == "wasm-opt") { ++ WasmOptPath = {}; + } + + if (!WasmOptPath.empty()) { +@@ -193,29 +187,27 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA, + ResponseFileSupport::AtFileCurCP(), + Linker, CmdArgs, Inputs, Output)); + +- if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { +- if (!WasmOptPath.empty()) { +- StringRef OOpt = "s"; +- if (A->getOption().matches(options::OPT_O4) || +- A->getOption().matches(options::OPT_Ofast)) +- OOpt = "4"; +- else if (A->getOption().matches(options::OPT_O0)) +- OOpt = "0"; +- else if (A->getOption().matches(options::OPT_O)) +- OOpt = A->getValue(); +- +- if (OOpt != "0") { +- const char *WasmOpt = Args.MakeArgString(WasmOptPath); +- ArgStringList OptArgs; +- OptArgs.push_back(Output.getFilename()); +- OptArgs.push_back(Args.MakeArgString(llvm::Twine("-O") + OOpt)); +- OptArgs.push_back("-o"); +- OptArgs.push_back(Output.getFilename()); +- C.addCommand(std::make_unique( +- JA, *this, ResponseFileSupport::AtFileCurCP(), WasmOpt, OptArgs, +- Inputs, Output)); +- } ++ if (!WasmOptPath.empty() && Args.hasArg(options::OPT_shared) && ++ llvm::sys::Process::GetEnv("WASM_SO_OPT")) { ++ const char *WasmOpt = Args.MakeArgString(WasmOptPath); ++ ArgStringList OptArgs; ++ OptArgs.push_back(Output.getFilename()); ++ OptArgs.push_back("-o"); ++ OptArgs.push_back(Output.getFilename()); ++ ++ std::istringstream iss(llvm::sys::Process::GetEnv("WASM_SO_OPT").value()); ++ std::string arg; ++ ++ while (iss >> arg) { ++ auto *arg_heap = new char[arg.size() + 1]; ++ std::copy(arg.begin(), arg.end(), arg_heap); ++ arg_heap[arg.size()] = '\0'; ++ OptArgs.push_back(arg_heap); + } ++ ++ C.addCommand(std::make_unique(JA, *this, ++ ResponseFileSupport::AtFileCurCP(), ++ WasmOpt, OptArgs, Inputs, Output)); + } + } + +diff --git a/compiler-rt/cmake/Modules/AddCompilerRT.cmake b/compiler-rt/cmake/Modules/AddCompilerRT.cmake +index c3e734f72392..282a321fd70b 100644 +--- a/compiler-rt/cmake/Modules/AddCompilerRT.cmake ++++ b/compiler-rt/cmake/Modules/AddCompilerRT.cmake +@@ -382,8 +382,8 @@ function(add_compiler_rt_runtime name type) + target_link_libraries(${libname} PRIVATE ${builtins_${libname}}) + endif() + if(${type} STREQUAL "SHARED") +- if(APPLE OR WIN32) +- set_property(TARGET ${libname} PROPERTY BUILD_WITH_INSTALL_RPATH ON) ++ if(COMMAND llvm_setup_rpath) ++ llvm_setup_rpath(${libname}) + endif() + if(WIN32 AND NOT CYGWIN AND NOT MINGW) + set_target_properties(${libname} PROPERTIES IMPORT_PREFIX "") +diff --git a/lld/wasm/InputChunks.cpp b/lld/wasm/InputChunks.cpp +index 0e6c4e691be1..bfa438505d98 100644 +--- a/lld/wasm/InputChunks.cpp ++++ b/lld/wasm/InputChunks.cpp +@@ -361,12 +361,11 @@ uint64_t InputChunk::getVA(uint64_t offset) const { + // Generate code to apply relocations to the data section at runtime. + // This is only called when generating shared libraries (PIC) where address are + // not known at static link time. +-bool InputChunk::generateRelocationCode(raw_ostream &os) const { ++void InputChunk::generateRelocationCode(std::vector &funcs) const { + LLVM_DEBUG(dbgs() << "generating runtime relocations: " << name + << " count=" << relocations.size() << "\n"); + + bool is64 = ctx.arg.is64.value_or(false); +- bool generated = false; + unsigned opcode_ptr_const = is64 ? WASM_OPCODE_I64_CONST + : WASM_OPCODE_I32_CONST; + unsigned opcode_ptr_add = is64 ? WASM_OPCODE_I64_ADD +@@ -385,6 +384,14 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const { + if (!requiresRuntimeReloc) + continue; + ++ if (funcs.empty() || funcs.back().size() >= 7654300) { ++ funcs.emplace_back(std::string()); ++ raw_string_ostream os(funcs.back()); ++ writeUleb128(os, 0, "num locals"); ++ } ++ ++ raw_string_ostream os(funcs.back()); ++ + LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type) + << " addend=" << rel.Addend << " index=" << rel.Index + << " output offset=" << offset << "\n"); +@@ -439,9 +446,7 @@ bool InputChunk::generateRelocationCode(raw_ostream &os) const { + writeU8(os, opcode_reloc_store, "I32_STORE"); + writeUleb128(os, 2, "align"); + writeUleb128(os, 0, "offset"); +- generated = true; + } +- return generated; + } + + // Split WASM_SEG_FLAG_STRINGS section. Such a section is a sequence of +diff --git a/lld/wasm/InputChunks.h b/lld/wasm/InputChunks.h +index f545449e1246..d231382a5f5e 100644 +--- a/lld/wasm/InputChunks.h ++++ b/lld/wasm/InputChunks.h +@@ -78,7 +78,7 @@ public: + + size_t getNumRelocations() const { return relocations.size(); } + void writeRelocations(llvm::raw_ostream &os) const; +- bool generateRelocationCode(raw_ostream &os) const; ++ void generateRelocationCode(std::vector &funcs) const; + + bool isTLS() const { return flags & llvm::wasm::WASM_SEG_FLAG_TLS; } + bool isRetained() const { return flags & llvm::wasm::WASM_SEG_FLAG_RETAIN; } +diff --git a/lld/wasm/SyntheticSections.cpp b/lld/wasm/SyntheticSections.cpp +index 0e2aa57e9048..e5df7d8c0be0 100644 +--- a/lld/wasm/SyntheticSections.cpp ++++ b/lld/wasm/SyntheticSections.cpp +@@ -299,6 +299,8 @@ void FunctionSection::writeBody() { + void FunctionSection::addFunction(InputFunction *func) { + if (!func->live) + return; ++ if (func->hasFunctionIndex()) ++ return; + uint32_t functionIndex = + out.importSec->getNumImportedFunctions() + inputFunctions.size(); + inputFunctions.emplace_back(func); +diff --git a/lld/wasm/Writer.cpp b/lld/wasm/Writer.cpp +index 2bf4b370a7db..19fca2616c7a 100644 +--- a/lld/wasm/Writer.cpp ++++ b/lld/wasm/Writer.cpp +@@ -1452,20 +1452,21 @@ void Writer::createStartFunction() { + void Writer::createApplyDataRelocationsFunction() { + LLVM_DEBUG(dbgs() << "createApplyDataRelocationsFunction\n"); + // First write the body's contents to a string. +- std::string bodyContent; ++ std::vector funcs; + { +- raw_string_ostream os(bodyContent); +- writeUleb128(os, 0, "num locals"); +- bool generated = false; + for (const OutputSegment *seg : segments) + if (!ctx.arg.sharedMemory || !seg->isTLS()) + for (const InputChunk *inSeg : seg->inputSegments) +- generated |= inSeg->generateRelocationCode(os); ++ inSeg->generateRelocationCode(funcs); ++ } + +- if (!generated) { +- LLVM_DEBUG(dbgs() << "skipping empty __wasm_apply_data_relocs\n"); +- return; +- } ++ if (funcs.empty()) { ++ LLVM_DEBUG(dbgs() << "skipping empty __wasm_apply_data_relocs\n"); ++ return; ++ } ++ ++ for (auto &func : funcs) { ++ raw_string_ostream os(func); + writeU8(os, WASM_OPCODE_END, "END"); + } + +@@ -1478,24 +1479,67 @@ void Writer::createApplyDataRelocationsFunction() { + make(nullSignature, "__wasm_apply_data_relocs")); + def->markLive(); + +- createFunction(def, bodyContent); ++ if (funcs.size() == 1) { ++ createFunction(def, funcs.back()); ++ return; ++ } ++ ++ std::string body; ++ { ++ raw_string_ostream os(body); ++ writeUleb128(os, 0, "num locals"); ++ ++ for (std::size_t i = 0; i < funcs.size(); ++i) { ++ auto &name = ++ *make("__wasm_apply_data_relocs_" + std::to_string(i)); ++ auto *func = make(nullSignature, name); ++ auto *def = symtab->addSyntheticFunction( ++ name, WASM_SYMBOL_VISIBILITY_HIDDEN, func); ++ def->markLive(); ++ // Normally this shouldn't be called manually for a synthetic ++ // function, since the function indices in ++ // ctx.syntheticFunctions will be calculated later (check ++ // functionSec->addFunction call hierarchy for details). ++ // However, at this point we already need the correct index. The ++ // solution is to place the new synthetic function eagerly, and ++ // also making addFunction idempotent by skipping when there's ++ // already a function index. ++ out.functionSec->addFunction(func); ++ createFunction(def, funcs[i]); ++ ++ writeU8(os, WASM_OPCODE_CALL, "CALL"); ++ writeUleb128(os, def->getFunctionIndex(), "function index"); ++ } ++ ++ writeU8(os, WASM_OPCODE_END, "END"); ++ } ++ createFunction(def, body); + } + + void Writer::createApplyTLSRelocationsFunction() { + LLVM_DEBUG(dbgs() << "createApplyTLSRelocationsFunction\n"); +- std::string bodyContent; ++ std::vector funcs; + { +- raw_string_ostream os(bodyContent); +- writeUleb128(os, 0, "num locals"); + for (const OutputSegment *seg : segments) + if (seg->isTLS()) + for (const InputChunk *inSeg : seg->inputSegments) +- inSeg->generateRelocationCode(os); ++ inSeg->generateRelocationCode(funcs); ++ } + ++ if (funcs.empty()) { ++ funcs.emplace_back(std::string()); ++ raw_string_ostream os(funcs.back()); ++ writeUleb128(os, 0, "num locals"); ++ } ++ ++ for (auto &func : funcs) { ++ raw_string_ostream os(func); + writeU8(os, WASM_OPCODE_END, "END"); + } + +- createFunction(ctx.sym.applyTLSRelocs, bodyContent); ++ assert(funcs.size() == 1); ++ ++ createFunction(ctx.sym.applyTLSRelocs, funcs.back()); + } + + // Similar to createApplyDataRelocationsFunction but generates relocation code +diff --git a/llvm/cmake/modules/AddLLVM.cmake b/llvm/cmake/modules/AddLLVM.cmake +index d3e9377c8d2f..50a34184919a 100644 +--- a/llvm/cmake/modules/AddLLVM.cmake ++++ b/llvm/cmake/modules/AddLLVM.cmake +@@ -2524,8 +2524,7 @@ function(llvm_setup_rpath name) + # FIXME: update this when there is better solution. + set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) + elseif(UNIX) +- set(_build_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) +- set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}") ++ set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) + if(${CMAKE_SYSTEM_NAME} MATCHES "(FreeBSD|DragonFly)") + set_property(TARGET ${name} APPEND_STRING PROPERTY + LINK_FLAGS " -Wl,-z,origin ") +@@ -2539,16 +2538,9 @@ function(llvm_setup_rpath name) + return() + endif() + +- # Enable BUILD_WITH_INSTALL_RPATH unless CMAKE_BUILD_RPATH is set and not +- # building for macOS or AIX, as those platforms seemingly require it. +- # On AIX, the tool chain doesn't support modifying rpaths/libpaths for XCOFF +- # on install at the moment, so BUILD_WITH_INSTALL_RPATH is required. ++ # Enable BUILD_WITH_INSTALL_RPATH unless CMAKE_BUILD_RPATH is set. + if("${CMAKE_BUILD_RPATH}" STREQUAL "") +- if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin|AIX") +- set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) +- else() +- set_property(TARGET ${name} APPEND PROPERTY BUILD_RPATH "${_build_rpath}") +- endif() ++ set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) + endif() + + set_target_properties(${name} PROPERTIES diff --git a/overlays/wasm.nix b/overlays/wasm.nix new file mode 100644 index 0000000000..b41e632bb6 --- /dev/null +++ b/overlays/wasm.nix @@ -0,0 +1,43 @@ +final: prev: prev.lib.optionalAttrs prev.stdenv.targetPlatform.isWasm { + llvmPackages = final.llvmPackages_20.override { + patchesFn = p: p // { "llvm/gnu-install-dirs.patch" = [{path = ./patches/wasm;}]; }; + monorepoSrc = + final.stdenv.mkDerivation { + pname = "llvm-source"; + version = final.llvmPackages_20.llvm.version + "-haskell"; + src = final.llvmPackages_20.llvm.monorepoSrc; + patches = ./patches/wasm/llvm/haskell-wasm-llvm-project.patch; + buildPhase = "true"; + installPhase = '' + cp -r . $out + ''; + }; + }; + wasilibc = prev.wasilibc.overrideAttrs (old: { + version = "25"; + src = final.buildPackages.fetchFromGitLab { + domain = "gitlab.haskell.org"; + owner = "haskell-wasm"; + repo = "wasi-libc"; + rev = "f8f0d3101e02aa3aaf37c5e31db23de34963053d"; + hash = "sha256-EvqbvVP9EH63C7KUmN4QaYjYbc4yGPU7vNev9u6a46o="; + fetchSubmodules = true; + }; + preBuild = '' + patchShebangs ./scripts + makeFlagsArray+=( + "default" + "libc_so" + ) + ''; + postBuild = '' + mkdir -p ${builtins.placeholder "out"} + mkdir -p ${builtins.placeholder "dev"} + mkdir -p ${builtins.placeholder "share"} + cp -r sysroot/lib/wasm32-wasi ${builtins.placeholder "out"}/lib + cp -r sysroot/include/wasm32-wasi ${builtins.placeholder "dev"}/include + cp -r sysroot/share/wasm32-wasi ${builtins.placeholder "share"}/share + ''; + nativeBuildInputs = old.nativeBuildInputs or [] ++ [ final.buildPackages.lld ]; + }); +} diff --git a/test/exe-dlls/default.nix b/test/exe-dlls/default.nix index 6d7a7ba475..f16960cbf2 100644 --- a/test/exe-dlls/default.nix +++ b/test/exe-dlls/default.nix @@ -14,7 +14,7 @@ let packages = project.hsPkgs; in recurseIntoAttrs rec { - meta.disabled = stdenv.hostPlatform.isGhcjs; + meta.disabled = stdenv.hostPlatform.isGhcjs || stdenv.hostPlatform.isWasm; ifdInputs = { inherit (project) plan-nix; diff --git a/test/exe-lib-dlls/default.nix b/test/exe-lib-dlls/default.nix index d2a883ca11..467c08afbe 100644 --- a/test/exe-lib-dlls/default.nix +++ b/test/exe-lib-dlls/default.nix @@ -14,7 +14,7 @@ let packages = project.hsPkgs; in recurseIntoAttrs rec { - meta.disabled = stdenv.hostPlatform.isGhcjs; + meta.disabled = stdenv.hostPlatform.isGhcjs || stdenv.hostPlatform.isWasm; ifdInputs = { inherit (project) plan-nix; diff --git a/test/gi-gtk/default.nix b/test/gi-gtk/default.nix index cdab43a1bb..8ceb1e1f69 100644 --- a/test/gi-gtk/default.nix +++ b/test/gi-gtk/default.nix @@ -16,7 +16,7 @@ let packages = project.hsPkgs; in recurseIntoAttrs rec { - meta.disabled = stdenv.hostPlatform.isGhcjs + meta.disabled = stdenv.hostPlatform.isGhcjs || stdenv.hostPlatform.isWasm # Gtk cross compilation seems to be broken in nixpkgs || stdenv.hostPlatform.isWindows # We can't make static libraries for Gtk diff --git a/test/th-dlls/default.nix b/test/th-dlls/default.nix index 33315dc011..08102dc72b 100644 --- a/test/th-dlls/default.nix +++ b/test/th-dlls/default.nix @@ -19,7 +19,7 @@ let packages-ei = (project true).hsPkgs; in recurseIntoAttrs { - meta.disabled = stdenv.hostPlatform.isGhcjs + meta.disabled = stdenv.hostPlatform.isGhcjs || stdenv.hostPlatform.isWasm # On aarch64 this test also breaks form musl builds (including cross compiles on x86_64-linux) || (stdenv.hostPlatform.isAarch64 && stdenv.hostPlatform.isMusl) # Not sure why this is failing with a seg fault