[clang] [llvm] [WebAssembly] Use Single as the default Triple thread model (PR #223917)

Thomas Lively via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 15 23:00:24 PDT 2026


https://github.com/tlively created https://github.com/llvm/llvm-project/pull/223917

Clang defaults WebAssembly to a single-threaded model unless atomics and bulk-memory are enabled, whereas Triple::getDefaultThreadModel() previously defaulted to POSIX for all targets. Because Clang only emits the "thread-model" module flag when the active thread model differs from the triple's default, single-threaded Wasm objects recorded "thread-model"="single" while multithreaded objects omitted the flag. When linking mixed objects in LTO, the merged module inherited "single", causing passes like LICM and AtomicExpand to treat multithreaded programs as single-threaded.

Change Triple::getDefaultThreadModel() to return ThreadModel::Single for WebAssembly so that multithreaded objects explicitly record "thread-model"= "posix" and single-threaded objects omit the flag. Additionally, update WebAssemblyCoalesceFeaturesAndStripAtomics to set the module thread model to POSIX when threading features are enabled and no explicit module flag is present, ensuring backend passes like AtomicExpandPass preserve atomics in raw IR modules compiled via llc.

Assisted-By: Gemini

>From 804a9390fdbb63cbb68e9b278055941824518f3b Mon Sep 17 00:00:00 2001
From: Thomas Lively <tlively at google.com>
Date: Tue, 15 Sep 2026 22:45:19 -0700
Subject: [PATCH] [WebAssembly] Use Single as the default Triple thread model

Clang defaults WebAssembly to a single-threaded model unless atomics and
bulk-memory are enabled, whereas Triple::getDefaultThreadModel() previously
defaulted to POSIX for all targets. Because Clang only emits the
"thread-model" module flag when the active thread model differs from the
triple's default, single-threaded Wasm objects recorded "thread-model"="single"
while multithreaded objects omitted the flag. When linking mixed objects in LTO,
the merged module inherited "single", causing passes like LICM and AtomicExpand
to treat multithreaded programs as single-threaded.

Change Triple::getDefaultThreadModel() to return ThreadModel::Single for
WebAssembly so that multithreaded objects explicitly record "thread-model"=
"posix" and single-threaded objects omit the flag. Additionally, update
WebAssemblyCoalesceFeaturesAndStripAtomics to set the module thread model to
POSIX when threading features are enabled and no explicit module flag is
present, ensuring backend passes like AtomicExpandPass preserve atomics in raw
IR modules compiled via llc.

Assisted-By: Gemini
---
 clang/test/CodeGen/thread-model.c                      |  8 ++++++++
 .../WebAssemblyCoalesceFeaturesAndStripAtomics.cpp     | 10 +++++++++-
 llvm/lib/TargetParser/Triple.cpp                       |  6 +++++-
 3 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/clang/test/CodeGen/thread-model.c b/clang/test/CodeGen/thread-model.c
index cf6eb82c948a0..419615fb40edb 100644
--- a/clang/test/CodeGen/thread-model.c
+++ b/clang/test/CodeGen/thread-model.c
@@ -5,7 +5,15 @@
 // RUN: %clang_cc1 -triple arm-none-linux-gnueabi -mthread-model posix -emit-llvm %s -o - | FileCheck %s --check-prefix=POSIX
 // RUN: %clang_cc1 -triple arm-none-linux-gnueabi -emit-llvm %s -o - | FileCheck %s --check-prefix=POSIX
 
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -mthread-model single -emit-llvm %s -o - | FileCheck %s --check-prefix=WASM-SINGLE
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +atomics -target-feature +bulk-memory -mthread-model posix -emit-llvm %s -o - | FileCheck %s --check-prefix=WASM-POSIX
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -target-feature +atomics -target-feature +bulk-memory -emit-llvm %s -o - | FileCheck %s --check-prefix=WASM-POSIX
+// RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm %s -o - | FileCheck %s --check-prefix=WASM-SINGLE
+
 void f(void) {}
 
 // SINGLE: !{i32 1, !"thread-model", !"single"}
 // POSIX-NOT: "thread-model"
+
+// WASM-SINGLE-NOT: "thread-model"
+// WASM-POSIX: !{i32 1, !"thread-model", !"posix"}
diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
index a040e531ee020..8c4b24806c9a9 100644
--- a/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
+++ b/llvm/lib/Target/WebAssembly/WebAssemblyCoalesceFeaturesAndStripAtomics.cpp
@@ -197,7 +197,15 @@ static bool coalesceFeaturesAndStripAtomics(Module &M,
   else if (StrippedTLS && !StrippedAtomics)
     stripAtomics(M);
 
-  recordFeatures(M, ST, Features, StrippedAtomics || StrippedTLS);
+  bool Stripped = StrippedAtomics || StrippedTLS;
+  if (!Stripped &&
+      (Features[WebAssembly::FeatureAtomics] ||
+       (CooperativeThreading && Features[WebAssembly::FeatureBulkMemory])) &&
+      !M.getModuleFlag("thread-model")) {
+    M.setThreadModel(ThreadModel::POSIX);
+  }
+
+  recordFeatures(M, ST, Features, Stripped);
 
   // Conservatively assume we have made some change
   return true;
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index ceabfe403e9c3..fb54408c0729f 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -2543,7 +2543,11 @@ FloatABI::ABIType Triple::getDefaultFloatABI() const {
   return FloatABI::Hard;
 }
 
-ThreadModel Triple::getDefaultThreadModel() const { return ThreadModel::POSIX; }
+ThreadModel Triple::getDefaultThreadModel() const {
+  if (isWasm())
+    return ThreadModel::Single;
+  return ThreadModel::POSIX;
+}
 
 LongDoubleFormat Triple::getDefaultLongDoubleFormat() const {
   switch (getArch()) {



More information about the llvm-commits mailing list