From 341e31a9979555a3445d96860e42f1b072a97129 Mon Sep 17 00:00:00 2001
From: Sam Clegg <sbc@chromium.org>
Date: Mon, 6 Jan 2025 11:37:14 -0800
Subject: [PATCH] Add initial support LLVM stable (v19)

Run the majority of the `other` under llvm stable.  There are still
some features that don't seem to work.

Generate and experimental warning for now, at least until we can fix
the known issues.

Fixes: #11362
---
 .circleci/config.yml | 49 ++++++++++++++++++++++++++++++++++++++++++++
 ChangeLog.md         |  3 +++
 emcc.py              |  8 ++++++++
 tools/shared.py      | 14 +++++++++++--
 4 files changed, 72 insertions(+), 2 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 4f64f4e80a7b1..662e159eebed2 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -796,6 +796,54 @@ jobs:
             core2.test_exceptions_wasm
             core2.test_pthread_unhandledrejection"
       - upload-test-results
+  test-llvm-stable:
+    executor: focal
+    environment:
+      LANG: "C.UTF-8"
+      EMTEST_SKIP_V8: "1"
+    steps:
+      - run:
+          name: install llvm
+          command: |
+            echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-19 main" >> /etc/apt/sources.list
+            wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+            apt-get update
+            apt-get install -q -y clang-19 lld-19
+      - checkout
+      - run:
+          name: submodule update
+          command: git submodule update --init
+      - pip-install
+      - install-emsdk
+      - install-rust
+      - run:
+          name: configure LLVM
+          command: echo "LLVM_ROOT='/usr/lib/llvm-19/bin'" >> ~/emsdk/.emscripten
+      - run: apt-get install -q -y ninja-build scons ccache
+      - run-tests:
+          extra-cflags: "-Wno-experimental"
+          # Would it make sense use a `@no_llvm_stable` decorator instead of
+          # listed in these here?
+          test_targets: "--skip-slow
+            core2.test_hello_world
+            other
+            skip:other.test_*code_size*
+            skip:other.test_*codesize*
+            skip:other.test_*memory64*
+            skip:other.test_*wasm64*
+            skip:other.test_fp16*
+            skip:other.test_wasm_worker*
+            skip:other.test_shared_memory*
+            skip:other.test_gen_struct_info*
+            skip:other.test_wasm_features
+            skip:other.test_post_link
+            skip:other.test_reproduce
+            skip:other.test_dylink_pthread_warning
+            skip:other.test_argument_missing
+            skip:other.test_embind_tsgen_worker_env
+            skip:other.test_explicit_target
+            skip:other.test_unistd_seek_wasmfs
+            "
   test-other:
     executor: focal
     environment:
@@ -1029,3 +1077,4 @@ workflows:
       - test-node-compat
       - test-windows
       - test-mac-arm64
+      - test-llvm-stable
diff --git a/ChangeLog.md b/ChangeLog.md
index 7bf5e0fca0385..c53994c3a04f0 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -52,6 +52,9 @@ See docs/process.md for more on how version tagging works.
 - The `POLYFILL_OLD_MATH_FUNCTIONS` setting was removed.  The browser versions
   that require these polyfills are no longer supported by emscripten so the
   polyfills should never be needed. (#23262)
+- Initial support was added for running emscripten with LLVM stable (currently
+  v19). This is currently experimental. (#23311)
+
 
 3.1.74 - 12/14/24
 -----------------
diff --git a/emcc.py b/emcc.py
index 1ce16b7d0d299..b62001d16f9cd 100644
--- a/emcc.py
+++ b/emcc.py
@@ -385,6 +385,14 @@ def get_clang_flags(user_args):
     if '-mbulk-memory' not in user_args:
       flags.append('-mbulk-memory')
 
+  if shared.is_llvm_stable():
+    # LLVM 19 doesn't enable these features by default, but we want
+    # them on-by-default, like they are on LLVM tot.
+    if '-mbulk-memory' not in user_args:
+      flags.append('-mbulk-memory')
+    if '-mnontrapping-fptoint' not in user_args:
+      flags.append('-mnontrapping-fptoint')
+
   if settings.RELOCATABLE and '-fPIC' not in user_args:
     flags.append('-fPIC')
 
diff --git a/tools/shared.py b/tools/shared.py
index 9ccd2354f83e1..e6efb89f3b0e7 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -60,6 +60,7 @@
 # This version currently matches the node version that we ship with emsdk
 # which means that we can say for sure that this version is well supported.
 MINIMUM_NODE_VERSION = (16, 20, 0)
+EXPECTED_LLVM_STABLE_VERSION = 19
 EXPECTED_LLVM_VERSION = 20
 
 # These get set by setup_temp_dirs
@@ -298,9 +299,18 @@ def get_clang_version():
   return m and m.group(1)
 
 
+def is_llvm_stable():
+  if SKIP_SUBPROCS:
+    return False
+  return get_clang_version().startswith('%d.' % EXPECTED_LLVM_STABLE_VERSION)
+
+
 def check_llvm_version():
+  if is_llvm_stable():
+    diagnostics.warning('experimental', f'Use of emscripten with LLVM stable (v{EXPECTED_LLVM_STABLE_VERSION}) is currently experimental, not all features are supported')
   actual = get_clang_version()
-  if actual.startswith('%d.' % EXPECTED_LLVM_VERSION):
+  expected = [EXPECTED_LLVM_VERSION, EXPECTED_LLVM_STABLE_VERSION]
+  if any(actual.startswith('%d.' % e) for e in expected):
     return True
   # When running in CI environment we also silently allow the next major
   # version of LLVM here so that new versions of LLVM can be rolled in
@@ -308,7 +318,7 @@ def check_llvm_version():
   if 'BUILDBOT_BUILDNUMBER' in os.environ:
     if actual.startswith('%d.' % (EXPECTED_LLVM_VERSION + 1)):
       return True
-  diagnostics.warning('version-check', 'LLVM version for clang executable "%s" appears incorrect (seeing "%s", expected "%s")', CLANG_CC, actual, EXPECTED_LLVM_VERSION)
+  diagnostics.warning('version-check', f'LLVM version for clang executable "{CLANG_CC}" appears incorrect (seeing "{actual}", expected "{EXPECTED_LLVM_VERSION}" or "{EXPECTED_LLVM_STABLE_VERSION}")')
   return False
 
 
