[clang] [WebAssembly] Enable musttail only when tail-call is enabled (PR #163618)

Jasmine Tang via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 31 15:19:20 PDT 2025


https://github.com/badumbatish updated https://github.com/llvm/llvm-project/pull/163618

>From f9c22f861739e52e77960ae8083490dfdf62d19e Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Wed, 15 Oct 2025 12:13:58 -0700
Subject: [PATCH 1/4] Precommit

---
 clang/test/CodeGen/WebAssembly/musttail.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
 create mode 100644 clang/test/CodeGen/WebAssembly/musttail.c

diff --git a/clang/test/CodeGen/WebAssembly/musttail.c b/clang/test/CodeGen/WebAssembly/musttail.c
new file mode 100644
index 0000000000000..da100484e94f2
--- /dev/null
+++ b/clang/test/CodeGen/WebAssembly/musttail.c
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -target-feature +tail-call -o /dev/null -emit-llvm -verify=good
+// RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -target-feature +tail-call -o /dev/null -emit-llvm -verify=good
+// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -o /dev/null -emit-llvm -verify=notail
+
+int foo(int x) {
+  return x;
+}
+
+#if __has_attribute(musttail)
+// good-warning at +2 {{HAS IT}}
+// notail-warning at +1 {{HAS IT}}
+#warning HAS IT
+#else
+#warning DOES NOT HAVE
+#endif
+
+int bar(int x)
+{
+ [[clang::musttail]] return foo(1);
+}

>From 26b9025acc1eaecd473196691ed5e23cfbabf88e Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Wed, 15 Oct 2025 12:14:04 -0700
Subject: [PATCH 2/4] Add must tail support

---
 clang/include/clang/Basic/Attr.td                  | 7 ++++++-
 clang/include/clang/Basic/DiagnosticCommonKinds.td | 2 ++
 clang/include/clang/Basic/TargetInfo.h             | 3 +++
 clang/lib/Basic/TargetInfo.cpp                     | 1 +
 clang/lib/Basic/Targets/WebAssembly.cpp            | 3 +++
 clang/lib/Sema/SemaStmt.cpp                        | 1 -
 clang/lib/Sema/SemaStmtAttr.cpp                    | 6 ++++++
 clang/test/CodeGen/WebAssembly/musttail.c          | 8 ++++----
 8 files changed, 25 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 3cde249e286fa..fab4e1dca024b 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -483,6 +483,7 @@ def TargetAnyX86 : TargetArch<["x86", "x86_64"]>;
 def TargetSPIRV : TargetArch<["spirv", "spirv32", "spirv64"]>;
 def TargetWebAssembly : TargetArch<["wasm32", "wasm64"]>;
 def TargetNVPTX : TargetArch<["nvptx", "nvptx64"]>;
+def TargetPowerPC : TargetArch<["ppc", "ppcle", "ppc64", "ppc64le"]>;
 def TargetWindows : TargetSpec {
   let OSes = ["Win32"];
 }
@@ -508,6 +509,10 @@ def TargetMicrosoftRecordLayout : TargetArch<["x86", "x86_64", "arm", "thumb",
   let CustomCode = [{ Target.hasMicrosoftRecordLayout() }];
 }
 
+def TargetMustTailAvaiable: TargetArch<!listconcat(TargetARM.Arches, TargetAArch64.Arches, TargetAnyX86.Arches, TargetWebAssembly.Arches, TargetPowerPC.Arches)> {
+  let CustomCode = [{ Target.hasMustTail() }];
+}
+
 def TargetELF : TargetSpec {
   let ObjectFormats = ["ELF"];
 }
@@ -1896,7 +1901,7 @@ def NoMerge : DeclOrStmtAttr {
                              "functions, statements and variables">;
 }
 
-def MustTail : StmtAttr {
+def MustTail : StmtAttr, TargetSpecificAttr<TargetMustTailAvaiable> {
   let Spellings = [Clang<"musttail">];
   let Documentation = [MustTailDocs];
   let Subjects = SubjectList<[ReturnStmt], ErrorDiag, "return statements">;
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index 6e50e225a8cc1..b0a928018cccd 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -374,6 +374,8 @@ def err_ppc_impossible_musttail: Error<
   >;
 def err_aix_musttail_unsupported: Error<
   "'musttail' attribute is not supported on AIX">;
+def err_wasm_musttail_unsupported: Error<
+  "'musttail' attribute is not supported on this target without tail-call feature">;
 
 // Source manager
 def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;
diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h
index ceb16174e13e7..e2768bea1902e 100644
--- a/clang/include/clang/Basic/TargetInfo.h
+++ b/clang/include/clang/Basic/TargetInfo.h
@@ -229,6 +229,7 @@ class TargetInfo : public TransferrableTargetInfo,
 protected:
   // Target values set by the ctor of the actual target implementation.  Default
   // values are specified by the TargetInfo constructor.
+  bool HasMustTail;
   bool BigEndian;
   bool TLSSupported;
   bool VLASupported;
@@ -669,6 +670,8 @@ class TargetInfo : public TransferrableTargetInfo,
                                        : getLongFractScale() + 1;
   }
 
+  virtual bool hasMustTail() const { return HasMustTail; }
+
   /// Determine whether the __int128 type is supported on this target.
   virtual bool hasInt128Type() const {
     return (getPointerWidth(LangAS::Default) >= 64) ||
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index f4d7c1288cc04..9a5db6e164f66 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -59,6 +59,7 @@ static const LangASMap FakeAddrSpaceMap = {
 TargetInfo::TargetInfo(const llvm::Triple &T) : Triple(T) {
   // Set defaults.  Defaults are set for a 32-bit RISC platform, like PPC or
   // SPARC.  These should be overridden by concrete targets as needed.
+  HasMustTail = true;
   BigEndian = !T.isLittleEndian();
   TLSSupported = true;
   VLASupported = true;
diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp
index 55ffe1df0ba08..5bbb7af4c2ca1 100644
--- a/clang/lib/Basic/Targets/WebAssembly.cpp
+++ b/clang/lib/Basic/Targets/WebAssembly.cpp
@@ -213,6 +213,7 @@ bool WebAssemblyTargetInfo::initFeatureMap(
 
 bool WebAssemblyTargetInfo::handleTargetFeatures(
     std::vector<std::string> &Features, DiagnosticsEngine &Diags) {
+  HasMustTail = false;
   for (const auto &Feature : Features) {
     if (Feature == "+atomics") {
       HasAtomics = true;
@@ -345,10 +346,12 @@ bool WebAssemblyTargetInfo::handleTargetFeatures(
     }
     if (Feature == "+tail-call") {
       HasTailCall = true;
+      HasMustTail = true;
       continue;
     }
     if (Feature == "-tail-call") {
       HasTailCall = false;
+      HasMustTail = false;
       continue;
     }
     if (Feature == "+wide-arithmetic") {
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index ae0bb616beb82..afd6fa617058b 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -695,7 +695,6 @@ bool Sema::checkMustTailAttr(const Stmt *St, const Attr &MTA) {
 
   const Expr *E = cast<ReturnStmt>(St)->getRetValue();
   const auto *CE = dyn_cast_or_null<CallExpr>(IgnoreParenImplicitAsWritten(E));
-
   if (!CE) {
     Diag(St->getBeginLoc(), diag::err_musttail_needs_call) << &MTA;
     return false;
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 50acc83f1841c..6ba8832d2e76a 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -672,6 +672,12 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
       !(A.existsInTarget(S.Context.getTargetInfo()) ||
         (S.Context.getLangOpts().SYCLIsDevice && Aux &&
          A.existsInTarget(*Aux)))) {
+    // Special case: musttail on WebAssembly without tail-call feature
+    if (A.getKind() == ParsedAttr::AT_MustTail &&
+        !S.Context.getTargetInfo().hasMustTail()) {
+      S.Diag(A.getLoc(), diag::err_wasm_musttail_unsupported);
+      return nullptr;
+    }
     if (A.isRegularKeywordAttribute()) {
       S.Diag(A.getLoc(), diag::err_keyword_not_supported_on_target)
           << A << A.getRange();
diff --git a/clang/test/CodeGen/WebAssembly/musttail.c b/clang/test/CodeGen/WebAssembly/musttail.c
index da100484e94f2..75ed7657a8087 100644
--- a/clang/test/CodeGen/WebAssembly/musttail.c
+++ b/clang/test/CodeGen/WebAssembly/musttail.c
@@ -1,5 +1,4 @@
-// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -target-feature +tail-call -o /dev/null -emit-llvm -verify=good
-// RUN: %clang_cc1 %s -triple wasm64-unknown-unknown -target-feature +tail-call -o /dev/null -emit-llvm -verify=good
+// RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -target-feature +tail-call -o /dev/null -emit-llvm -verify=tail
 // RUN: %clang_cc1 %s -triple wasm32-unknown-unknown -o /dev/null -emit-llvm -verify=notail
 
 int foo(int x) {
@@ -7,14 +6,15 @@ int foo(int x) {
 }
 
 #if __has_attribute(musttail)
-// good-warning at +2 {{HAS IT}}
-// notail-warning at +1 {{HAS IT}}
+// tail-warning at +1 {{HAS IT}}
 #warning HAS IT
 #else
+// notail-warning at +1 {{DOES NOT HAVE}}
 #warning DOES NOT HAVE
 #endif
 
 int bar(int x)
 {
+  // notail-error at +1 {{'musttail' attribute is not supported on this target without tail-call feature}}
  [[clang::musttail]] return foo(1);
 }

>From 4e4c93923a7d8f774ab85c958fb8b42703653cae Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Fri, 31 Oct 2025 14:29:01 -0700
Subject: [PATCH 3/4] Address PR comments

---
 clang/include/clang/Basic/Attr.td                  | 3 +--
 clang/include/clang/Basic/DiagnosticCommonKinds.td | 2 --
 clang/lib/Sema/SemaStmt.cpp                        | 1 +
 clang/lib/Sema/SemaStmtAttr.cpp                    | 6 ------
 clang/test/CodeGen/WebAssembly/musttail.c          | 2 +-
 5 files changed, 3 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index fab4e1dca024b..a4e0899799871 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -483,7 +483,6 @@ def TargetAnyX86 : TargetArch<["x86", "x86_64"]>;
 def TargetSPIRV : TargetArch<["spirv", "spirv32", "spirv64"]>;
 def TargetWebAssembly : TargetArch<["wasm32", "wasm64"]>;
 def TargetNVPTX : TargetArch<["nvptx", "nvptx64"]>;
-def TargetPowerPC : TargetArch<["ppc", "ppcle", "ppc64", "ppc64le"]>;
 def TargetWindows : TargetSpec {
   let OSes = ["Win32"];
 }
@@ -509,7 +508,7 @@ def TargetMicrosoftRecordLayout : TargetArch<["x86", "x86_64", "arm", "thumb",
   let CustomCode = [{ Target.hasMicrosoftRecordLayout() }];
 }
 
-def TargetMustTailAvaiable: TargetArch<!listconcat(TargetARM.Arches, TargetAArch64.Arches, TargetAnyX86.Arches, TargetWebAssembly.Arches, TargetPowerPC.Arches)> {
+def TargetMustTailAvaiable:  TargetSpec {
   let CustomCode = [{ Target.hasMustTail() }];
 }
 
diff --git a/clang/include/clang/Basic/DiagnosticCommonKinds.td b/clang/include/clang/Basic/DiagnosticCommonKinds.td
index b0a928018cccd..6e50e225a8cc1 100644
--- a/clang/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/clang/include/clang/Basic/DiagnosticCommonKinds.td
@@ -374,8 +374,6 @@ def err_ppc_impossible_musttail: Error<
   >;
 def err_aix_musttail_unsupported: Error<
   "'musttail' attribute is not supported on AIX">;
-def err_wasm_musttail_unsupported: Error<
-  "'musttail' attribute is not supported on this target without tail-call feature">;
 
 // Source manager
 def err_cannot_open_file : Error<"cannot open file '%0': %1">, DefaultFatal;
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index afd6fa617058b..ae0bb616beb82 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -695,6 +695,7 @@ bool Sema::checkMustTailAttr(const Stmt *St, const Attr &MTA) {
 
   const Expr *E = cast<ReturnStmt>(St)->getRetValue();
   const auto *CE = dyn_cast_or_null<CallExpr>(IgnoreParenImplicitAsWritten(E));
+
   if (!CE) {
     Diag(St->getBeginLoc(), diag::err_musttail_needs_call) << &MTA;
     return false;
diff --git a/clang/lib/Sema/SemaStmtAttr.cpp b/clang/lib/Sema/SemaStmtAttr.cpp
index 6ba8832d2e76a..50acc83f1841c 100644
--- a/clang/lib/Sema/SemaStmtAttr.cpp
+++ b/clang/lib/Sema/SemaStmtAttr.cpp
@@ -672,12 +672,6 @@ static Attr *ProcessStmtAttribute(Sema &S, Stmt *St, const ParsedAttr &A,
       !(A.existsInTarget(S.Context.getTargetInfo()) ||
         (S.Context.getLangOpts().SYCLIsDevice && Aux &&
          A.existsInTarget(*Aux)))) {
-    // Special case: musttail on WebAssembly without tail-call feature
-    if (A.getKind() == ParsedAttr::AT_MustTail &&
-        !S.Context.getTargetInfo().hasMustTail()) {
-      S.Diag(A.getLoc(), diag::err_wasm_musttail_unsupported);
-      return nullptr;
-    }
     if (A.isRegularKeywordAttribute()) {
       S.Diag(A.getLoc(), diag::err_keyword_not_supported_on_target)
           << A << A.getRange();
diff --git a/clang/test/CodeGen/WebAssembly/musttail.c b/clang/test/CodeGen/WebAssembly/musttail.c
index 75ed7657a8087..37fed70028bbc 100644
--- a/clang/test/CodeGen/WebAssembly/musttail.c
+++ b/clang/test/CodeGen/WebAssembly/musttail.c
@@ -15,6 +15,6 @@ int foo(int x) {
 
 int bar(int x)
 {
-  // notail-error at +1 {{'musttail' attribute is not supported on this target without tail-call feature}}
+  // notail-warning at +1 {{unknown attribute 'clang::musttail' ignored}}
  [[clang::musttail]] return foo(1);
 }

>From 7ea334eee72e0f24e7955f4c3c5b6bc98b496e42 Mon Sep 17 00:00:00 2001
From: Jasmine Tang <jjasmine at igalia.com>
Date: Fri, 31 Oct 2025 15:18:41 -0700
Subject: [PATCH 4/4] Add to changelog

---
 clang/docs/ReleaseNotes.rst | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 65b086caf3652..ffd2881453894 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -546,6 +546,8 @@ NetBSD Support
 WebAssembly Support
 ^^^^^^^^^^^^^^^^^^^
 
+- Fix a bug so that __has_attribute(musttail) should not be true anymore when notail is not enabled.
+
 AVR Support
 ^^^^^^^^^^^
 



More information about the cfe-commits mailing list