Repository: pyodide/pyodide

Issue:
Migrate to upstream LLVM for emscripten backend
It looks like the now recommended compiler backend for emscripten is the upstream LLVM instead of fastcomp + asm2wasm (https://github.com/WebAssembly/binaryen/issues/2177#issuecomment-504269479). 

This might also help with scipy dynamic linking, as the section of code that errors now in https://github.com/iodide-project/pyodide/pull/473 will no longer be run.

~~There is no tagged emsdk release with it yet, I think, but hopefully, one be released soon.~~

I don't have a very good understanding of how much work this would involve. I'm hoping existing emsdk patches would still apply. Maybe it would be necessary to update to the latest emsdk release first. 


Discussion:
Some WIP on this can be found in the https://github.com/iodide-project/pyodide/compare/master...rth:llvm-upstream branch. For one thing `EMULATED_FUNCTION_POINTERS` are not allowed when `WASM_BACKEND=1` (I'm not sure if they are still needed or not).

Also on that branch, the build fails with  the following errors,
<details>

```
em++ -s EXPORT_NAME="'pyodide'" -o build/pyodide.asm.html src/main.bc src/jsimport.bc src/jsproxy.bc src/js2python.bc src/pyimport.bc src/pyproxy.bc src/python2js.bc src/python2js_buffer.bc src/runpython.bc src/hiwire.bc \
  -O3 -s MODULARIZE=1 cpython/installs/python-3.7.0/lib/libpython3.7.a lz4/lz4-1.8.3/lib/liblz4.a -s "BINARYEN_METHOD='native-wasm'" -s TOTAL_MEMORY=1073741824 -s ALLOW_MEMORY_GROWTH=1 -s MAIN_MODULE=1 -s LINKABLE=1 -s EXPORT_ALL=1 -s EXPORTED_FUNCTIONS='["___cxa_guard_acquire", "__ZNSt3__28ios_base4initEPv"]' -s WASM=1 -s SWAPPABLE_ASM_MODULE=1 -s USE_FREETYPE=1 -s USE_LIBPNG=1 -std=c++14 -Lcpython/build/sqlite-autoconf-3270200/.libs -lsqlite3 -lstdc++ --memory-init-file 0 -s "BINARYEN_TRAP_MODE='clamp'" -s TEXTDECODER=0 -s LZ4=1 -s FORCE_FILESYSTEM=1
wasm-ld: error: lz4_5d1337a7.o: machine type must be wasm32
wasm-ld: error: lz4frame_75f13d14.o: machine type must be wasm32
wasm-ld: error: lz4hc_43143d2b.o: machine type must be wasm32
wasm-ld: error: xxhash_96cdb457.o: machine type must be wasm32
wasm-ld: error: sqlite3_6234be2a.o: machine type must be wasm32
shared:ERROR: '/src/emsdk/emsdk/upstream/bin/wasm-ld -o /tmp/emscripten_temp_hkjy2pic/pyodide.asm.wasm --allow-undefined --import-memory --import-table --lto-O0 --whole-archive src/main.bc src/jsimport.bc src/jsproxy.bc src/js2python.bc src/pyimport.bc src/pyproxy.bc src/python2js.bc src/python2js_buffer.bc src/runpython.bc src/hiwire.bc cpython/installs/python-3.7.0/lib/libpython3.7.a lz4/lz4-1.8.3/lib/liblz4.a cpython/build/sqlite-autoconf-3270200/.libs/libsqlite3.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libfreetype.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libpng.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libz.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libc++_noexcept.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libc++abi.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libc.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libcompiler_rt.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libc-wasm.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libdlmalloc.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libpthreads_stub.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libc-extras.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libcompiler_rt_wasm.a /src/emsdk/emsdk/.emscripten_cache/wasm-obj-pic/libc_rt_wasm.a --no-whole-archive -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr --no-gc-sections --export-dynamic --export-all -pie -z stack-size=5242880 --initial-memory=1073741824 --no-entry' failed (1)
Makefile:64: recipe for target 'build/pyodide.asm.js' failed
make: *** [build/pyodide.asm.js] Error 1
```
</details>
Possibly related to https://github.com/emscripten-core/emscripten/issues/8321
hi,

>  EMULATED_FUNCTION_POINTERS are not allowed when WASM_BACKEND=1(I'm not sure if they are still needed or not).

no they are no more required since python 3.8+ has the required fixes.

could be https://bugs.python.org/issue33012

side note function pointers were broken on fastcomp when using dynamic linking but it has been fixed on latest- emsdk.

but dynamic linking is still problematic on upstream with -fPIC
To move forward on this I think it might help doing the update more incrementally,
 1. first update from 1.38.30 to  1.38.35 - that's the first release with the new  emscripten build system, but with very minor changes in emscripten. Here the build, at least for CPython is working, there is just some issue to resolve when importing pyodide (and likely the fact that we might still need to patch [binaryen due for num_params](https://github.com/iodide-project/pyodide/blob/master/emsdk/patches/num_params.patch)) https://github.com/iodide-project/pyodide/pull/480
 2. Update to the latest emscripten still using the fastcomp compiler. 
 3. Switch to the LLVM upstream compiler in emscripten  .

I think directly updating to 3 as we tried to do in https://github.com/iodide-project/pyodide/pull/531 https://github.com/iodide-project/pyodide/pull/637 might require a bit too much work in one PR. 
A quick status update on this,
 - for the incremental approach outlined in the above comment we are still blocked at step 1. Building with emscripten 1.38.35 and fastcomp backend works but there is an issue with importing numpy https://github.com/iodide-project/pyodide/pull/480#issuecomment-635823062 (also opened https://github.com/numpy/numpy/issues/16804 about it, in case someone at numpy has suggestions, though it's probably a bit off topic for the numpy repo)
 - for the non incremental approach (i.e. https://github.com/iodide-project/pyodide/pull/637 I updated it with latest master) there might be still things missing from https://github.com/iodide-project/pyodide/pull/531 and I couldn't find fixes by @manumartin discussed there. It sounds like we would need at least 1.38.42 there. The main concern with this approach, is that
   - we can resolve build issues, however if something fail at import/run time, it's harder to determine which emscripten change is responsible as there is a long list of changes to go through.
   - generally it would require a lot of non interrupted attention, with the risk of abandoning before we made it to fully work.

In any case this likely one of the most critical issues for pyodide at the moment, as it's blocking a number of other topics. Help would be very much appreciated.

cc @sconover @daicoden
Thanks to @dalcde the migration to emsdk 1.38.34 is now done. 

When [updating to 1.38.35](https://github.com/iodide-project/pyodide/compare/master...rth:emsdk-1.38.35?expand=1) there another issue apparently, this time with loading of the main pyodide package.  There are no errors but the the load  with `languagePluginLoader` never completes (it keeps using CPU resources so maybe an infinite loop somewhere). I need to investigate this.

It used to be relatively straightforward to update the emsdk versions. 
My experience with the update (I tried 1.38.37) is that it actually
hangs after some time, consuming no CPU resources (or at least very
little).

Upgrading to 1.38.38 results in another breakage during compile time,
where emconfigure doesn't recognize that certain math.h functions (e.g.
acos) are present.

The latter breakage involves a fatal error in the asm2wasm script (it
complains about a legitimate problem but didn't use to be fatal);
migrating to the llvm backend might help (there is another asm2wasm
breakage that occurs when updating binaryen).

> I tried 1.38.37) is that it actually
hangs after some time, consuming no CPU resources (or at least very
little).

Yes, indeed it's the same issue. I'm just saying that it first appeared in 1.38.35.

> migrating to the llvm backend might help

In previous attemps I think we needed at least 1.38.42 to do that, as for earlier versions there are some compilation issues that an only be solved by updating emsdk. 

At the moment, as far as I can tell, dynamic linking together with EMULATE_FUNCTION_POINTER_CASTS is broken with the llvm backend, see https://github.com/emscripten-core/emscripten/issues/13076 . I don't think we would be able to progress until that is fixed.
@joemarshall Has reported some progress on this subject in https://gitter.im/Wasm-Python/community?at=5fe7057ac746c6431cd929eb It would very interesting to try to integrate some of [your changes](https://github.com/joemarshall/pyodide/commits/master) into pyodide. 


Oh yeah, just getting round to looking at this. I have upstream 2.0.9 building and running on my repo https://github.com/joemarshall/pyodide now. There's a surprising amount of patching though, main things that needed fixed are:
1) use built in file packager (I see that's fixed in pyodide now)
2) change build to make .o files and .so files correctly
3) fix function signatures across scipy, numpy and clapack to be consistent. This is due partly to f2c handling subroutines by converting them into functions returning int 0, whereas some code in numpy and scipy expects them to return void, and also a bunch of code in scipy that uses implicit declarations that resolve differently to how they are declared. C doesn't care about this normally but in linking for wasm these are errors because the wasm stack machine checks arguments and return types.
Oh and the patch to binaryen so that fpcast emulation doesn't break dlopen and dlsym.
That's great @joemarshall !

> This is due partly to f2c handling subroutines by converting them into functions returning int 0, whereas some code in numpy and scipy expects them to return void,

Good to know, we will probably have to live with those patches since normal numpy/scipy builds don't use f2c

> also a bunch of code in scipy that uses implicit declarations that resolve differently to how they are declared. C doesn't care about this normally but in linking for wasm these are errors because the wasm stack machine checks arguments and return types

We could try to upstream those, assuming they apply to parts of the scipy code base that hasn't evolved much, since the version we use which is quite outdated.

From you comment on Gitter,
> Incidentally, do you know the current plan for switching across to upstream from fastcomp? Would an upstream branch make sense? Just that a lot of the fixes I did, like all the messing with clapack, numpy and scipy to get rid of conflicting function signatures and make .so files build right, i think they will break fastcomp builds.

yes, we could create an `upstream-llvm` branch and accept PRs there if it helps, then merge it (without squashing) into master. Though if there are some fixes that we can merge into master independently that would be good as well. WDYT @dalcde ?  
> we will probably have to live with those patches since normal numpy/scipy builds don't use f2c

Actually it looks like it might be possible to use flang with the WASM target using LLVM 11+, https://github.com/iodide-project/pyodide/issues/184 so we might be able get rid of all f2c fixes and replace CLAPACK by BLAS + newer LAPACK. Though we still need a working build on master with the llvm backend as a starting point.  
I've got the latest llvm and flang (that used to be f18) doesn't build code yet.

In theory old flang might support new enough llvm to build wasm, but it is 64 bit only right now.

--------------------

Note: I am currently part time, my working days are Wednesday to Friday. Responses may be delayed outside these times.

On 3 Jan 2021 17:03, Roman Yurchak <notifications@github.com> wrote:

we will probably have to live with those patches since normal numpy/scipy builds don't use f2c

Actually it looks like it might be possible to use flang with the WASM target using LLVM 11+, #184<https://github.com/iodide-project/pyodide/issues/184> so we might be able get rid of all f2c fixes and replace CLAPACK by BLAS + newer LAPACK. Though we still need a working build on master with the llvm backend as a starting point.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<https://github.com/iodide-project/pyodide/issues/476#issuecomment-753646499>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAK6Y65O6VS7SL3MX7QZHYLSYCPOBANCNFSM4H2TQEJA>.



This message and any attachment are intended solely for the addressee
and may contain confidential information. If you have received this
message in error, please contact the sender and delete the email and
attachment. 

Any views or opinions expressed by the author of this email do not
necessarily reflect the views of the University of Nottingham. Email
communications with the University of Nottingham may be monitored 
where permitted by law.





And there's also a version of GCC with a wasm backend, which in theory should compile Fortran too. Not had time to play with it though.
Various repos relating to this on

https://github.com/pipcet

--------------------

Note: I am currently part time, my working days are Wednesday to Friday. Responses may be delayed outside these times.

On 3 Jan 2021 21:56, Joe Marshall <pszjm2@exmail.nottingham.ac.uk> wrote:
I've got the latest llvm and flang (that used to be f18) doesn't build code yet.

In theory old flang might support new enough llvm to build wasm, but it is 64 bit only right now.

--------------------

Note: I am currently part time, my working days are Wednesday to Friday. Responses may be delayed outside these times.

On 3 Jan 2021 17:03, Roman Yurchak <notifications@github.com> wrote:

we will probably have to live with those patches since normal numpy/scipy builds don't use f2c

Actually it looks like it might be possible to use flang with the WASM target using LLVM 11+, #184<https://github.com/iodide-project/pyodide/issues/184> so we might be able get rid of all f2c fixes and replace CLAPACK by BLAS + newer LAPACK. Though we still need a working build on master with the llvm backend as a starting point.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub<https://github.com/iodide-project/pyodide/issues/476#issuecomment-753646499>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AAK6Y65O6VS7SL3MX7QZHYLSYCPOBANCNFSM4H2TQEJA>.



This message and any attachment are intended solely for the addressee
and may contain confidential information. If you have received this
message in error, please contact the sender and delete the email and
attachment. 

Any views or opinions expressed by the author of this email do not
necessarily reflect the views of the University of Nottingham. Email
communications with the University of Nottingham may be monitored 
where permitted by law.






Resolving patch:
diff --git a/pyodide_build/buildpkg.py b/pyodide_build/buildpkg.py
--- a/pyodide_build/buildpkg.py
+++ b/pyodide_build/buildpkg.py
@@ -215,10 +215,12 @@ def package_files(buildpath: Path, srcpath: Path, pkg: Dict[str, Any], args):
 
 
 def run_script(buildpath: Path, srcpath: Path, pkg: Dict[str, Any]):
-    # We don't really do packaging, but needs_rebuild checks .packaged to
-    # determine if it needs to rebuild
-    if (buildpath / ".packaged").is_file():
-        return
+    if pkg.get("build", {}).get("library"):
+        # in libraries this  writes the packaged flag
+        # We don't really do packaging, but needs_rebuild checks .packaged to
+        # determine if it needs to rebuild
+        if (buildpath / ".packaged").is_file():
+            return
 
     orig_path = Path.cwd()
     os.chdir(srcpath)
@@ -275,11 +277,9 @@ def build_package(path: Path, args):
             os.makedirs(buildpath)
         srcpath = download_and_extract(buildpath, packagedir, pkg, args)
         patch(path, srcpath, pkg, args)
-        if pkg.get("build", {}).get("library"):
+        if pkg.get("build", {}).get("script"):
             run_script(buildpath, srcpath, pkg)
-        else:
-            if pkg.get("build", {}).get("script"):
-                run_script(buildpath, srcpath, pkg)
+        if not pkg.get("build", {}).get("library", False):
             compile(path, srcpath, pkg, args)
             package_files(buildpath, srcpath, pkg, args)
     finally:
diff --git a/pyodide_build/common.py b/pyodide_build/common.py
--- a/pyodide_build/common.py
+++ b/pyodide_build/common.py
@@ -15,9 +15,9 @@
         "-O2",
         "-Werror",
         "-s", "EMULATE_FUNCTION_POINTER_CASTS=1",
+        "-s",'BINARYEN_EXTRA_PASSES="--pass-arg=max-func-params@61"',
         "-s", "SIDE_MODULE=1",
         "-s", "WASM=1",
-        "-s", "BINARYEN_TRAP_MODE='clamp'",
         "--memory-init-file", "0",
         "-s", "LINKABLE=1",
         "-s", "EXPORT_ALL=1",
diff --git a/pyodide_build/pywasmcross.py b/pyodide_build/pywasmcross.py
--- a/pyodide_build/pywasmcross.py
+++ b/pyodide_build/pywasmcross.py
@@ -27,7 +27,7 @@
 import importlib.machinery
 import json
 import os
-from pathlib import Path
+from pathlib import Path, PurePosixPath
 import re
 import subprocess
 import shutil
@@ -252,7 +252,10 @@ def handle_command(line, args, dryrun=False):
         # distutils doesn't use the c++ compiler when compiling c++ <sigh>
         if any(arg.endswith((".cpp", ".cc")) for arg in line):
             new_args = ["em++"]
-    library_output = line[-1].endswith(".so")
+    library_output = False
+    for arg in line:
+        if arg.endswith(".so") and not arg.startswith("-"):
+            library_output = True
 
     if library_output:
         new_args.extend(args.ldflags.split())
@@ -263,6 +266,8 @@ def handle_command(line, args, dryrun=False):
 
     lapack_dir = None
 
+    used_libs = set()
+
     # Go through and adjust arguments
     for arg in line[1:]:
         if arg.startswith("-I"):
@@ -278,9 +283,19 @@ def handle_command(line, args, dryrun=False):
         if arg.startswith("-L/usr"):
             continue
         if arg.startswith("-l"):
-            if arg[2:] in replace_libs:
-                arg = "-l" + replace_libs[arg[2:]]
-
+            for lib_name in replace_libs.keys():
+                # this enables glob style **/* matching
+                if PurePosixPath(arg[2:]).match(lib_name):
+                    if len(replace_libs[lib_name]) > 0:
+                        arg = "-l" + replace_libs[lib_name]
+                    else:
+                        continue
+        if arg.startswith("-l"):
+            # WASM link doesn't like libraries being included twice
+            # skip second one
+            if arg in used_libs:
+                continue
+            used_libs.add(arg)
         # threading is disabled for now
         if arg == "-pthread":
             continue
@@ -291,8 +306,14 @@ def handle_command(line, args, dryrun=False):
         # definitely isn't
         arg = re.sub(r"/python([0-9]\.[0-9]+)m", r"/python\1", arg)
         if arg.endswith(".so"):
-            arg = arg[:-3] + ".wasm"
             output = arg
+        # don't include libraries from native builds
+        if (
+            len(args.install_dir) > 0
+            and arg.startswith("-l" + args.install_dir)
+            or arg.startswith("-L" + args.install_dir)
+        ):
+            continue
 
         # Fix for scipy to link to the correct BLAS/LAPACK files
         if arg.startswith("-L") and "CLAPACK" in arg:
@@ -362,14 +383,14 @@ def handle_command(line, args, dryrun=False):
 
     # Emscripten .so files shouldn't have the native platform slug
     if library_output:
-        renamed = output[:-5] + ".so"
+        renamed = output
         for ext in importlib.machinery.EXTENSION_SUFFIXES:
             if ext == ".so":
                 continue
             if renamed.endswith(ext):
                 renamed = renamed[: -len(ext)] + ".so"
                 break
-        if not dryrun:
+        if not dryrun and output != renamed:
             os.rename(output, renamed)
     return new_args
 


Test patch:
diff --git a/emsdk/tests/common.py b/emsdk/tests/common.py
--- a/emsdk/tests/common.py
+++ b/emsdk/tests/common.py
@@ -1,6 +1,15 @@
-MAIN_C = """
+MAIN_C = r"""
 #include <stdio.h>
 #include <dlfcn.h>
+#include <setjmp.h>
+
+void never_called()
+{
+    jmp_buf buf;
+    int i=setjmp(buf);
+    longjmp(buf,1);
+}
+
 
 int main() {
   puts("hello from main");
diff --git a/emsdk/tests/test_dyncall.py b/emsdk/tests/test_dyncall.py
--- a/emsdk/tests/test_dyncall.py
+++ b/emsdk/tests/test_dyncall.py
@@ -33,6 +33,7 @@ def test_dyncall(tmpdir):
   assert(fp(i, 0, 0, 0) == i);
 
   if (i == 0) longjmp(buf, 1);
+
 }
 """
             )
@@ -42,6 +43,7 @@ def test_dyncall(tmpdir):
         subprocess.run(
             [
                 "emcc",
+                "-g4",
                 "-s",
                 "SIDE_MODULE=1",
                 "library.c",
@@ -57,6 +59,7 @@ def test_dyncall(tmpdir):
         subprocess.run(
             [
                 "emcc",
+                "-g4",
                 "-s",
                 "MAIN_MODULE=1",
                 "main.c",
@@ -68,4 +71,4 @@ def test_dyncall(tmpdir):
             check=True,
         )
         out = subprocess.run(["node", "a.out.js"], capture_output=True, check=True)
-        assert out.stdout == b"hello from main\n0\n4\n"
+        assert out.stdout == b"hello from main\n0\n1\n"
diff --git a/emsdk/tests/test_emulate.py b/emsdk/tests/test_emulate.py
--- a/emsdk/tests/test_emulate.py
+++ b/emsdk/tests/test_emulate.py
@@ -9,8 +9,11 @@ def test_emulate_function(tmpdir):
                 """\
 #include <stdio.h>
 
-void foo() {
+// emulate function pointer casts - this has the
+// wrong arguments and return type
+int foo(int extra_args) {
   puts("hello from library");
+  return 0;
 }"""
             )
         with open("main.c", "w") as f:
@@ -19,6 +22,7 @@ def test_emulate_function(tmpdir):
         subprocess.run(
             [
                 "emcc",
+                "-g",
                 "-s",
                 "SIDE_MODULE=1",
                 "library.c",
@@ -34,6 +38,7 @@ def test_emulate_function(tmpdir):
         subprocess.run(
             [
                 "emcc",
+                "-g",
                 "-s",
                 "MAIN_MODULE=1",
                 "main.c",
@@ -41,6 +46,8 @@ def test_emulate_function(tmpdir):
                 "library.wasm",
                 "-s",
                 "EMULATE_FUNCTION_POINTER_CASTS=1",
+                "-s",
+                "EXPORT_ALL=1",
             ],
             check=True,
         )
diff --git a/packages/joblib/test_joblib.py b/packages/joblib/test_joblib.py
--- a/packages/joblib/test_joblib.py
+++ b/packages/joblib/test_joblib.py
@@ -2,8 +2,6 @@
 
 
 def test_joblib_numpy_pickle(selenium, request):
-    if selenium.browser == "chrome":
-        request.applymarker(pytest.mark.xfail(run=False, reason="chrome not supported"))
     selenium.load_package(["numpy", "joblib"])
     selenium.run(
         """
diff --git a/packages/pandas/test_pandas.py b/packages/pandas/test_pandas.py
--- a/packages/pandas/test_pandas.py
+++ b/packages/pandas/test_pandas.py
@@ -35,15 +35,11 @@ def generate_largish_json(n_rows: int = 91746) -> Dict:
 
 
 def test_pandas(selenium, request):
-    if selenium.browser == "chrome":
-        request.applymarker(pytest.mark.xfail(run=False, reason="chrome not supported"))
     selenium.load_package("pandas")
     assert len(selenium.run("import pandas\ndir(pandas)")) == 142
 
 
 def test_extra_import(selenium, request):
-    if selenium.browser == "chrome":
-        request.applymarker(pytest.mark.xfail(run=False, reason="chrome not supported"))
 
     selenium.load_package("pandas")
     selenium.run("from pandas import Series, DataFrame, Panel")
@@ -52,9 +48,6 @@ def test_extra_import(selenium, request):
 def test_load_largish_file(selenium_standalone, request, httpserver):
     selenium = selenium_standalone
 
-    if selenium.browser == "chrome":
-        request.applymarker(pytest.mark.xfail(run=False, reason="chrome not supported"))
-
     selenium.load_package("pandas")
     selenium.load_package("matplotlib")
 
diff --git a/packages/scikit-learn/test_scikit-learn.py b/packages/scikit-learn/test_scikit-learn.py
--- a/packages/scikit-learn/test_scikit-learn.py
+++ b/packages/scikit-learn/test_scikit-learn.py
@@ -3,8 +3,6 @@
 
 def test_scikit_learn(selenium_standalone, request):
     selenium = selenium_standalone
-    if selenium.browser == "chrome":
-        request.applymarker(pytest.mark.xfail(run=False, reason="chrome not supported"))
     selenium.load_package("scikit-learn")
     assert (
         selenium.run(
diff --git a/packages/scipy/test_scipy.py b/packages/scipy/test_scipy.py
--- a/packages/scipy/test_scipy.py
+++ b/packages/scipy/test_scipy.py
@@ -6,9 +6,6 @@
 def test_scipy_linalg(selenium_standalone, request):
     selenium = selenium_standalone
 
-    if selenium.browser == "chrome":
-        request.applymarker(pytest.mark.xfail(run=False, reason="chrome not supported"))
-
     selenium.load_package("scipy")
     cmd = dedent(
         r"""
diff --git a/packages/test_common.py b/packages/test_common.py
--- a/packages/test_common.py
+++ b/packages/test_common.py
@@ -26,7 +26,7 @@ def registered_packages_meta():
     }
 
 
-UNSUPPORTED_PACKAGES = {"chrome": ["pandas", "scipy", "scikit-learn"], "firefox": []}
+UNSUPPORTED_PACKAGES = {"chrome": [], "firefox": []}
 
 
 @pytest.mark.parametrize("name", registered_packages())
@@ -68,34 +68,9 @@ def test_import(name, selenium_standalone):
         ))
         """
     )
-
-    selenium_standalone.load_package(name)
-
-    # Make sure there are no additional .pyc file
-    assert (
-        selenium_standalone.run(
-            """
-        len(list(glob.glob(
-            '/lib/python3.8/site-packages/**/*.pyc',
-            recursive=True)
-        ))
-        """
-        )
-        == baseline_pyc
-    )
-
     loaded_packages = []
     for import_name in meta.get("test", {}).get("imports", []):
-
-        if name not in loaded_packages:
-            selenium_standalone.load_package(name)
-            loaded_packages.append(name)
-        try:
-            selenium_standalone.run("import %s" % import_name)
-        except Exception:
-            print(selenium_standalone.logs)
-            raise
-
+        selenium_standalone.run_async("import %s" % import_name)
         # Make sure that even after importing, there are no additional .pyc
         # files
         assert (
diff --git a/pyodide_build/tests/test_pywasmcross.py b/pyodide_build/tests/test_pywasmcross.py
--- a/pyodide_build/tests/test_pywasmcross.py
+++ b/pyodide_build/tests/test_pywasmcross.py
@@ -43,7 +43,7 @@ def test_handle_command():
     assert handle_command_wrap("gcc test.c", args) == "emcc test.c"
     assert (
         handle_command_wrap("gcc -shared -c test.o -o test.so", args)
-        == "emcc -c test.o -o test.wasm"
+        == "emcc -c test.o -o test.so"
     )
 
     # check cxxflags injection and cpp detection
@@ -66,7 +66,7 @@ def test_handle_command():
     )
     assert (
         handle_command_wrap("gcc -shared -c test.o -o test.so", args)
-        == "emcc -lm -c test.o -o test.wasm"
+        == "emcc -lm -c test.o -o test.so"
     )
 
     # check library replacement and removal of double libraries
@@ -80,7 +80,7 @@ def test_handle_command():
     )
     assert (
         handle_command_wrap("gcc -shared test.o -lbob -ljim -ljim -o test.so", args)
-        == "emcc test.o -lfred -ljim -ljim -o test.wasm"
+        == "emcc test.o -lfred -ljim -o test.so"
     )
 
     # compilation checks in numpy
@@ -106,7 +106,7 @@ def test_conda_compiler_compat():
     )
     assert handle_command_wrap(
         "gcc -shared -c test.o -B /compiler_compat -o test.so", args
-    ) == ("emcc -c test.o -o test.wasm")
+    ) == ("emcc -c test.o -o test.so")
 
 
 def test_environment_var_substitution(monkeypatch):