[lld] [llvm] [MC][WebAssembly] Allow strings for import modules and names in asm (PR #182896)

Sy Brand via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 3 04:02:40 PST 2026


https://github.com/TartanLlama updated https://github.com/llvm/llvm-project/pull/182896

>From 38138759c356bc9129431753c95b4c08edfba89a Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Mon, 23 Feb 2026 16:49:06 +0000
Subject: [PATCH 01/12] Allow strings for import modules and names

---
 .../AsmParser/WebAssemblyAsmParser.cpp        | 23 ++++++++++++--
 .../WebAssemblyTargetStreamer.cpp             | 31 +++++++++++++++----
 .../MC/WebAssembly/import-module-invalid.s    |  2 +-
 llvm/test/MC/WebAssembly/import-module.s      | 14 +++++++++
 .../test/MC/WebAssembly/import-name-invalid.s |  2 +-
 5 files changed, 61 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index 9175b2731dac0..b48abe85aca91 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -415,6 +415,23 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
     return Name;
   }
 
+  StringRef expectStringOrIdent() {
+    if (Lexer.is(AsmToken::String)) {
+      auto Str = Lexer.getTok().getString();
+      Parser.Lex();
+      Str.consume_front("\"");
+      Str.consume_back("\"");
+      return Str;
+    }
+    if (Lexer.is(AsmToken::Identifier)) {
+      auto Name = Lexer.getTok().getString();
+      Parser.Lex();
+      return Name;
+    }
+    error("Expected string or identifier, got: ", Lexer.getTok());
+    return StringRef();
+  }
+
   bool parseRegTypeList(SmallVectorImpl<wasm::ValType> &Types) {
     while (Lexer.is(AsmToken::Identifier)) {
       auto Type = WebAssembly::parseType(Lexer.getTok().getString());
@@ -1041,7 +1058,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
         return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
         return ParseStatus::Failure;
-      auto ExportName = expectIdent();
+      auto ExportName = expectStringOrIdent();
       if (ExportName.empty())
         return ParseStatus::Failure;
       auto *WasmSym =
@@ -1057,7 +1074,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
         return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
         return ParseStatus::Failure;
-      auto ImportModule = expectIdent();
+      auto ImportModule = expectStringOrIdent();
       if (ImportModule.empty())
         return ParseStatus::Failure;
       auto *WasmSym =
@@ -1073,7 +1090,7 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
         return ParseStatus::Failure;
       if (expect(AsmToken::Comma, ","))
         return ParseStatus::Failure;
-      auto ImportName = expectIdent();
+      StringRef ImportName = expectStringOrIdent();
       if (ImportName.empty())
         return ParseStatus::Failure;
       auto *WasmSym =
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index c18de5fc1939e..916a42664279f 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -22,6 +22,15 @@
 #include "llvm/Support/FormattedStream.h"
 using namespace llvm;
 
+static bool shouldQuoteName(StringRef Name) {
+  // Wasm export/import names and import module names can contain characters
+  // that are not allowed in identifiers, so we need to quote them.
+  auto isValidStartChar = [](char C) { return isalpha(C) || C == '_' || C == '.'; };
+  auto isValidChar = [&](char C) { return isValidStartChar(C) || isdigit(C); };
+  return !(Name.size() > 0 && isValidStartChar(Name.front())) || 
+        llvm::any_of(Name, [&](char C) { return !isValidChar(C); });
+}
+
 WebAssemblyTargetStreamer::WebAssemblyTargetStreamer(MCStreamer &S)
     : MCTargetStreamer(S) {}
 
@@ -94,22 +103,32 @@ void WebAssemblyTargetAsmStreamer::emitTagType(const MCSymbolWasm *Sym) {
   OS << "\n";
 }
 
+static void emitNameWithQuoting(formatted_raw_ostream &OS, StringRef Name) {
+  if (shouldQuoteName(Name))
+    OS << '"' << Name << '"';
+  else
+    OS << Name;
+}
+
 void WebAssemblyTargetAsmStreamer::emitImportModule(const MCSymbolWasm *Sym,
                                                     StringRef ImportModule) {
-  OS << "\t.import_module\t" << Sym->getName() << ", "
-                             << ImportModule << '\n';
+  OS << "\t.import_module\t" << Sym->getName() << ", ";
+  emitNameWithQuoting(OS, ImportModule);
+  OS << '\n';
 }
 
 void WebAssemblyTargetAsmStreamer::emitImportName(const MCSymbolWasm *Sym,
                                                   StringRef ImportName) {
-  OS << "\t.import_name\t" << Sym->getName() << ", "
-                           << ImportName << '\n';
+  OS << "\t.import_name\t" << Sym->getName() << ", ";
+  emitNameWithQuoting(OS, ImportName);
+  OS << '\n';
 }
 
 void WebAssemblyTargetAsmStreamer::emitExportName(const MCSymbolWasm *Sym,
                                                   StringRef ExportName) {
-  OS << "\t.export_name\t" << Sym->getName() << ", "
-                           << ExportName << '\n';
+  OS << "\t.export_name\t" << Sym->getName() << ", ";
+  emitNameWithQuoting(OS, ExportName);
+  OS << '\n';
 }
 
 void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) {
diff --git a/llvm/test/MC/WebAssembly/import-module-invalid.s b/llvm/test/MC/WebAssembly/import-module-invalid.s
index a9f93e83dba39..daf18f2e6a505 100644
--- a/llvm/test/MC/WebAssembly/import-module-invalid.s
+++ b/llvm/test/MC/WebAssembly/import-module-invalid.s
@@ -6,7 +6,7 @@
 # CHECK: [[#@LINE+1]]:19: error: Expected ,, instead got:
 .import_module foo
 
-# CHECK: [[#@LINE+1]]:20: error: Expected identifier, got:
+# CHECK: [[#@LINE+1]]:20: error: Expected string or identifier, got:
 .import_module foo,
 
 # CHECK: [[#@LINE+1]]:24: error: Expected EOL, instead got: ,
diff --git a/llvm/test/MC/WebAssembly/import-module.s b/llvm/test/MC/WebAssembly/import-module.s
index 5d28d5b9c0b92..75090001755a9 100644
--- a/llvm/test/MC/WebAssembly/import-module.s
+++ b/llvm/test/MC/WebAssembly/import-module.s
@@ -3,16 +3,22 @@
 
 .functype foo () -> ()
 .functype plain () -> ()
+.functype __wasm_component_model_builtin_context_get_0 () -> (i32)
 
 test:
   .functype test () -> ()
   call      foo
   call      plain
+  call     __wasm_component_model_builtin_context_get_0
+  drop
   end_function
 
   .import_module  foo, bar
   .import_name  foo, qux
 
+  .import_module __wasm_component_model_builtin_context_get_0, "$root"
+  .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
+
 # CHECK-ASM: .import_module  foo, bar
 # CHECK-ASM: .import_name  foo, qux
 
@@ -26,9 +32,17 @@ test:
 # CHECK-NEXT:         Field:           plain
 # CHECK-NEXT:         Kind:            FUNCTION
 
+# CHECK:            - Module:          '$root'
+# CHECK-NEXT:         Field:           '[context-get-0]'
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         SigIndex:        1
+
 # CHECK:        - Type:            CUSTOM
 # CHECK:              Name:            foo
 # CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
 
 # CHECK:              Name:            plain
 # CHECK-NEXT:         Flags:           [ UNDEFINED ]
+
+# CHECK:              Name:            __wasm_component_model_builtin_context_get_0
+# CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
\ No newline at end of file
diff --git a/llvm/test/MC/WebAssembly/import-name-invalid.s b/llvm/test/MC/WebAssembly/import-name-invalid.s
index da8ed0d4617b0..df709f6380a06 100644
--- a/llvm/test/MC/WebAssembly/import-name-invalid.s
+++ b/llvm/test/MC/WebAssembly/import-name-invalid.s
@@ -6,7 +6,7 @@
 # CHECK: [[#@LINE+1]]:17: error: Expected ,, instead got:
 .import_name foo
 
-# CHECK: [[#@LINE+1]]:18: error: Expected identifier, got:
+# CHECK: [[#@LINE+1]]:18: error: Expected string or identifier, got:
 .import_name foo,
 
 # CHECK: [[#@LINE+1]]:22: error: Expected EOL, instead got: ,

>From 11a9778a689c1121b75e76b5e635a5382c78ae7a Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Mon, 23 Feb 2026 16:50:02 +0000
Subject: [PATCH 02/12] Newline

---
 llvm/test/MC/WebAssembly/import-module.s | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/MC/WebAssembly/import-module.s b/llvm/test/MC/WebAssembly/import-module.s
index 75090001755a9..5aa41f166e6c7 100644
--- a/llvm/test/MC/WebAssembly/import-module.s
+++ b/llvm/test/MC/WebAssembly/import-module.s
@@ -45,4 +45,4 @@ test:
 # CHECK-NEXT:         Flags:           [ UNDEFINED ]
 
 # CHECK:              Name:            __wasm_component_model_builtin_context_get_0
-# CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
\ No newline at end of file
+# CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]

>From 6c1411c1211a2e0070cb40066ad893a23b51eb75 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Mon, 23 Feb 2026 17:16:56 +0000
Subject: [PATCH 03/12] More tests

---
 llvm/test/MC/WebAssembly/export-name-invalid.s   | 5 ++++-
 llvm/test/MC/WebAssembly/export-name.s           | 7 +++++++
 llvm/test/MC/WebAssembly/import-module-invalid.s | 3 +++
 llvm/test/MC/WebAssembly/import-name-invalid.s   | 3 +++
 4 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/llvm/test/MC/WebAssembly/export-name-invalid.s b/llvm/test/MC/WebAssembly/export-name-invalid.s
index ad322ce949f5e..e6e1d2dfa8a8a 100644
--- a/llvm/test/MC/WebAssembly/export-name-invalid.s
+++ b/llvm/test/MC/WebAssembly/export-name-invalid.s
@@ -6,8 +6,11 @@
 # CHECK: [[#@LINE+1]]:17: error: Expected ,, instead got:
 .export_name foo
 
-# CHECK: [[#@LINE+1]]:18: error: Expected identifier, got:
+# CHECK: [[#@LINE+1]]:18: error: Expected string or identifier, got:
 .export_name foo,
 
 # CHECK: [[#@LINE+1]]:22: error: Expected EOL, instead got: ,
 .export_name foo, bar,
+
+# CHECK: [[#@LINE+1]]:19: error: Expected string or identifier, got: "bar
+.export_name foo, "bar
\ No newline at end of file
diff --git a/llvm/test/MC/WebAssembly/export-name.s b/llvm/test/MC/WebAssembly/export-name.s
index 51e1bcf73dba2..d9f058a21660f 100644
--- a/llvm/test/MC/WebAssembly/export-name.s
+++ b/llvm/test/MC/WebAssembly/export-name.s
@@ -8,7 +8,14 @@ foo:
     .export_name foo, bar
     end_function
 
+square:
+    .globl square
+    .functype square () -> ()
+    .export_name square, "[square]"
+    end_function
+
 # CHECK: .export_name foo, bar
+# CHECK: .export_name square, "[square]"
 
 # CHECK-OBJ:        - Type:            EXPORT
 # CHECK-OBJ-NEXT:     Exports:
diff --git a/llvm/test/MC/WebAssembly/import-module-invalid.s b/llvm/test/MC/WebAssembly/import-module-invalid.s
index daf18f2e6a505..55745ea911b7e 100644
--- a/llvm/test/MC/WebAssembly/import-module-invalid.s
+++ b/llvm/test/MC/WebAssembly/import-module-invalid.s
@@ -11,3 +11,6 @@
 
 # CHECK: [[#@LINE+1]]:24: error: Expected EOL, instead got: ,
 .import_module foo, bar,
+
+# CHECK: [[#@LINE+1]]:21: error: Expected string or identifier, got: "bar
+.import_module foo, "bar
diff --git a/llvm/test/MC/WebAssembly/import-name-invalid.s b/llvm/test/MC/WebAssembly/import-name-invalid.s
index df709f6380a06..841c55eee3a18 100644
--- a/llvm/test/MC/WebAssembly/import-name-invalid.s
+++ b/llvm/test/MC/WebAssembly/import-name-invalid.s
@@ -11,3 +11,6 @@
 
 # CHECK: [[#@LINE+1]]:22: error: Expected EOL, instead got: ,
 .import_name foo, bar,
+
+# CHECK: [[#@LINE+1]]:19: error: Expected string or identifier, got: "bar
+.import_name foo, "bar

>From 4f1340601ed73b96a65d1acbb8af2b11f9f765b7 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Mon, 23 Feb 2026 17:18:25 +0000
Subject: [PATCH 04/12] Newline

---
 llvm/test/MC/WebAssembly/export-name-invalid.s | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/test/MC/WebAssembly/export-name-invalid.s b/llvm/test/MC/WebAssembly/export-name-invalid.s
index e6e1d2dfa8a8a..31144dc16bf14 100644
--- a/llvm/test/MC/WebAssembly/export-name-invalid.s
+++ b/llvm/test/MC/WebAssembly/export-name-invalid.s
@@ -13,4 +13,4 @@
 .export_name foo, bar,
 
 # CHECK: [[#@LINE+1]]:19: error: Expected string or identifier, got: "bar
-.export_name foo, "bar
\ No newline at end of file
+.export_name foo, "bar

>From 8660f198ae5981e27a939cc547a3f63fb6934fd5 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 25 Feb 2026 08:48:53 +0000
Subject: [PATCH 05/12] Allow dollars in identifiers

---
 .../AsmParser/WebAssemblyAsmParser.cpp        |  4 +--
 .../MCTargetDesc/WebAssemblyMCAsmInfo.cpp     |  2 ++
 .../WebAssemblyTargetStreamer.cpp             |  2 +-
 llvm/test/MC/WebAssembly/export-name.s        | 29 +++++++++++++++++++
 llvm/test/MC/WebAssembly/import-module.s      | 14 +++++++++
 5 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
index b48abe85aca91..79673f80701bb 100644
--- a/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
+++ b/llvm/lib/Target/WebAssembly/AsmParser/WebAssemblyAsmParser.cpp
@@ -417,10 +417,8 @@ class WebAssemblyAsmParser final : public MCTargetAsmParser {
 
   StringRef expectStringOrIdent() {
     if (Lexer.is(AsmToken::String)) {
-      auto Str = Lexer.getTok().getString();
+      auto Str = Lexer.getTok().getStringContents();
       Parser.Lex();
-      Str.consume_front("\"");
-      Str.consume_back("\"");
       return Str;
     }
     if (Lexer.is(AsmToken::Identifier)) {
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
index a783e5fd8521a..a69f0eb1db196 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
@@ -57,5 +57,7 @@ WebAssemblyMCAsmInfo::WebAssemblyMCAsmInfo(const Triple &T,
   SupportsDebugInformation = true;
   ExceptionsType = ExceptionHandling::None;
 
+  AllowDollarAtStartOfIdentifier = true;
+
   initializeAtSpecifiers(atSpecifiers);
 }
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index 916a42664279f..0e7f4614b7d36 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -25,7 +25,7 @@ using namespace llvm;
 static bool shouldQuoteName(StringRef Name) {
   // Wasm export/import names and import module names can contain characters
   // that are not allowed in identifiers, so we need to quote them.
-  auto isValidStartChar = [](char C) { return isalpha(C) || C == '_' || C == '.'; };
+  auto isValidStartChar = [](char C) { return isalpha(C) || C == '_' || C == '.' || C == '$'; };
   auto isValidChar = [&](char C) { return isValidStartChar(C) || isdigit(C); };
   return !(Name.size() > 0 && isValidStartChar(Name.front())) || 
         llvm::any_of(Name, [&](char C) { return !isValidChar(C); });
diff --git a/llvm/test/MC/WebAssembly/export-name.s b/llvm/test/MC/WebAssembly/export-name.s
index d9f058a21660f..23cce3fd762ee 100644
--- a/llvm/test/MC/WebAssembly/export-name.s
+++ b/llvm/test/MC/WebAssembly/export-name.s
@@ -14,8 +14,22 @@ square:
     .export_name square, "[square]"
     end_function
 
+$dollar:
+    .globl $dollar
+    .functype $dollar () -> ()
+    .export_name $dollar, $dollar
+    end_function
+
+mid$dollar:
+    .globl mid$dollar
+    .functype mid$dollar () -> ()
+    .export_name mid$dollar, mid$dollar
+    end_function
+
 # CHECK: .export_name foo, bar
 # CHECK: .export_name square, "[square]"
+# CHECK: .export_name $dollar, $dollar
+# CHECK: .export_name mid$dollar, mid$dollar
 
 # CHECK-OBJ:        - Type:            EXPORT
 # CHECK-OBJ-NEXT:     Exports:
@@ -31,3 +45,18 @@ square:
 # CHECK-OBJ-NEXT:         Name:            foo
 # CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
 # CHECK-OBJ-NEXT:         Function:        0
+# CHECK-OBJ-NEXT:       - Index:           1
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Name:            square
+# CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
+# CHECK-OBJ-NEXT:         Function:        1
+# CHECK-OBJ-NEXT:       - Index:           2
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Name:            '$dollar'
+# CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
+# CHECK-OBJ-NEXT:         Function:        2
+# CHECK-OBJ-NEXT:       - Index:           3
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Name:            'mid$dollar'
+# CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
+# CHECK-OBJ-NEXT:         Function:        3
diff --git a/llvm/test/MC/WebAssembly/import-module.s b/llvm/test/MC/WebAssembly/import-module.s
index 5aa41f166e6c7..110496fdb399e 100644
--- a/llvm/test/MC/WebAssembly/import-module.s
+++ b/llvm/test/MC/WebAssembly/import-module.s
@@ -4,6 +4,7 @@
 .functype foo () -> ()
 .functype plain () -> ()
 .functype __wasm_component_model_builtin_context_get_0 () -> (i32)
+.functype __wasm_component_model_builtin_context_get_1 () -> (i32)
 
 test:
   .functype test () -> ()
@@ -11,6 +12,8 @@ test:
   call      plain
   call     __wasm_component_model_builtin_context_get_0
   drop
+  call     __wasm_component_model_builtin_context_get_1
+  drop
   end_function
 
   .import_module  foo, bar
@@ -19,6 +22,9 @@ test:
   .import_module __wasm_component_model_builtin_context_get_0, "$root"
   .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
 
+  .import_module __wasm_component_model_builtin_context_get_1, $root
+  .import_name __wasm_component_model_builtin_context_get_1, "[context-get-1]"
+
 # CHECK-ASM: .import_module  foo, bar
 # CHECK-ASM: .import_name  foo, qux
 
@@ -37,6 +43,11 @@ test:
 # CHECK-NEXT:         Kind:            FUNCTION
 # CHECK-NEXT:         SigIndex:        1
 
+# CHECK:            - Module:          '$root'
+# CHECK-NEXT:         Field:           '[context-get-1]'
+# CHECK-NEXT:         Kind:            FUNCTION
+# CHECK-NEXT:         SigIndex:        1
+
 # CHECK:        - Type:            CUSTOM
 # CHECK:              Name:            foo
 # CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
@@ -46,3 +57,6 @@ test:
 
 # CHECK:              Name:            __wasm_component_model_builtin_context_get_0
 # CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
+
+# CHECK:              Name:            __wasm_component_model_builtin_context_get_1
+# CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
\ No newline at end of file

>From 45a0db289edd414b7a77b0e9bfa694cf87f28c7a Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Wed, 25 Feb 2026 09:12:24 +0000
Subject: [PATCH 06/12] Add dollar symbol lld test

---
 lld/test/wasm/dollar-symbol.s | 109 ++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)
 create mode 100644 lld/test/wasm/dollar-symbol.s

diff --git a/lld/test/wasm/dollar-symbol.s b/lld/test/wasm/dollar-symbol.s
new file mode 100644
index 0000000000000..e35e7b4b797c2
--- /dev/null
+++ b/lld/test/wasm/dollar-symbol.s
@@ -0,0 +1,109 @@
+## Test linking of symbols with dollar signs in their names.
+# RUN: split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/dollar.o %t/dollar.s
+# RUN: obj2yaml %t/dollar.o | FileCheck %s --check-prefix=CHECK-OBJ-DOLLAR
+# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/main.o %t/main.s
+# RUN: obj2yaml %t/main.o | FileCheck %s --check-prefix=CHECK-OBJ-MAIN
+# RUN: wasm-ld %t/dollar.o %t/main.o -o %t/out.wasm 2>&1
+# RUN: obj2yaml %t/out.wasm | FileCheck %s --check-prefix=CHECK-OUT
+
+# CHECK-OBJ-DOLLAR:     SymbolTable:
+# CHECK-OBJ-DOLLAR-NEXT:      - Index:           0
+# CHECK-OBJ-DOLLAR-NEXT:        Kind:            FUNCTION
+# CHECK-OBJ-DOLLAR-NEXT:        Name:            '$dollar_func'
+# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
+# CHECK-OBJ-DOLLAR-NEXT:        Function:        0
+# CHECK-OBJ-DOLLAR-NEXT:      - Index:           1
+# CHECK-OBJ-DOLLAR-NEXT:        Kind:            FUNCTION
+# CHECK-OBJ-DOLLAR-NEXT:        Name:            'mid$dollar_func'
+# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
+# CHECK-OBJ-DOLLAR-NEXT:        Function:        1
+# CHECK-OBJ-DOLLAR-NEXT:      - Index:           2
+# CHECK-OBJ-DOLLAR-NEXT:        Kind:            DATA
+# CHECK-OBJ-DOLLAR-NEXT:        Name:            '$dollar_global'
+# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
+# CHECK-OBJ-DOLLAR-NEXT:        Segment:         0
+# CHECK-OBJ-DOLLAR-NEXT:        Size:            4
+# CHECK-OBJ-DOLLAR-NEXT:      - Index:           3
+# CHECK-OBJ-DOLLAR-NEXT:        Kind:            DATA
+# CHECK-OBJ-DOLLAR-NEXT:        Name:            'mid$dollar_global'
+# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
+# CHECK-OBJ-DOLLAR-NEXT:        Segment:         1
+# CHECK-OBJ-DOLLAR-NEXT:        Size:            4
+
+# CHECK-OBJ-MAIN:    SymbolTable:
+# CHECK-OBJ-MAIN-NEXT:      - Index:           0
+# CHECK-OBJ-MAIN-NEXT:        Kind:            DATA
+# CHECK-OBJ-MAIN-NEXT:        Name:            '$dollar_global'
+# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
+# CHECK-OBJ-MAIN-NEXT:      - Index:           1
+# CHECK-OBJ-MAIN-NEXT:        Kind:            DATA
+# CHECK-OBJ-MAIN-NEXT:        Name:            'mid$dollar_global'
+# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
+# CHECK-OBJ-MAIN-NEXT:      - Index:           2
+# CHECK-OBJ-MAIN-NEXT:        Kind:            FUNCTION
+# CHECK-OBJ-MAIN-NEXT:        Name:            _start
+# CHECK-OBJ-MAIN-NEXT:        Flags:           [  ]
+# CHECK-OBJ-MAIN-NEXT:        Function:        2
+# CHECK-OBJ-MAIN-NEXT:      - Index:           3
+# CHECK-OBJ-MAIN-NEXT:        Kind:            FUNCTION
+# CHECK-OBJ-MAIN-NEXT:        Name:            '$dollar_func'
+# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
+# CHECK-OBJ-MAIN-NEXT:        Function:        0
+# CHECK-OBJ-MAIN-NEXT:      - Index:           4
+# CHECK-OBJ-MAIN-NEXT:        Kind:            FUNCTION
+# CHECK-OBJ-MAIN-NEXT:        Name:            'mid$dollar_func'
+# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
+# CHECK-OBJ-MAIN-NEXT:        Function:        1
+
+# CHECK-OUT:        - Type:            CUSTOM
+# CHECK-OUT-NEXT:        Name:            name
+# CHECK-OUT-NEXT:        FunctionNames:
+# CHECK-OUT-NEXT:          - Index:           0
+# CHECK-OUT-NEXT:            Name:            '$dollar_func'
+# CHECK-OUT-NEXT:          - Index:           1
+# CHECK-OUT-NEXT:            Name:            'mid$dollar_func'
+# CHECK-OUT-NEXT:          - Index:           2
+
+#--- main.s
+
+.functype $dollar_func () -> ()
+.functype mid$dollar_func () -> ()
+
+.globl $dollar_global
+.globl mid$dollar_global
+
+.globl _start
+_start:
+  .functype _start () -> ()
+  call $dollar_func
+  call mid$dollar_func
+  i32.const $dollar_global
+  drop
+  i32.const mid$dollar_global
+  drop
+  end_function
+
+#--- dollar.s
+
+.globl $dollar_func
+$dollar_func:
+  .functype $dollar_func () -> ()
+  end_function
+
+.globl mid$dollar_func
+mid$dollar_func:
+  .functype mid$dollar_func () -> ()
+  end_function
+
+.globl $dollar_global
+.section  ".data.$dollar_global","",@
+$dollar_global:
+  .int32 42
+  .size $dollar_global, 4
+
+.globl mid$dollar_global
+.section  ".data.mid$dollar_global","",@
+mid$dollar_global:
+  .int32 43
+  .size mid$dollar_global, 4

>From fb81b6bb5c7a1df29b8346ba2e412bbd9e9f0a48 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Thu, 26 Feb 2026 08:24:04 +0000
Subject: [PATCH 07/12] Revert dollar changes

---
 lld/test/wasm/dollar-symbol.s                 | 109 ------------------
 .../MCTargetDesc/WebAssemblyMCAsmInfo.cpp     |   2 -
 .../WebAssemblyTargetStreamer.cpp             |   4 +-
 llvm/test/MC/WebAssembly/export-name.s        |  14 +--
 llvm/test/MC/WebAssembly/import-module.s      |  11 --
 5 files changed, 3 insertions(+), 137 deletions(-)
 delete mode 100644 lld/test/wasm/dollar-symbol.s

diff --git a/lld/test/wasm/dollar-symbol.s b/lld/test/wasm/dollar-symbol.s
deleted file mode 100644
index e35e7b4b797c2..0000000000000
--- a/lld/test/wasm/dollar-symbol.s
+++ /dev/null
@@ -1,109 +0,0 @@
-## Test linking of symbols with dollar signs in their names.
-# RUN: split-file %s %t
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/dollar.o %t/dollar.s
-# RUN: obj2yaml %t/dollar.o | FileCheck %s --check-prefix=CHECK-OBJ-DOLLAR
-# RUN: llvm-mc -filetype=obj -triple=wasm32-unknown-unknown -o %t/main.o %t/main.s
-# RUN: obj2yaml %t/main.o | FileCheck %s --check-prefix=CHECK-OBJ-MAIN
-# RUN: wasm-ld %t/dollar.o %t/main.o -o %t/out.wasm 2>&1
-# RUN: obj2yaml %t/out.wasm | FileCheck %s --check-prefix=CHECK-OUT
-
-# CHECK-OBJ-DOLLAR:     SymbolTable:
-# CHECK-OBJ-DOLLAR-NEXT:      - Index:           0
-# CHECK-OBJ-DOLLAR-NEXT:        Kind:            FUNCTION
-# CHECK-OBJ-DOLLAR-NEXT:        Name:            '$dollar_func'
-# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
-# CHECK-OBJ-DOLLAR-NEXT:        Function:        0
-# CHECK-OBJ-DOLLAR-NEXT:      - Index:           1
-# CHECK-OBJ-DOLLAR-NEXT:        Kind:            FUNCTION
-# CHECK-OBJ-DOLLAR-NEXT:        Name:            'mid$dollar_func'
-# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
-# CHECK-OBJ-DOLLAR-NEXT:        Function:        1
-# CHECK-OBJ-DOLLAR-NEXT:      - Index:           2
-# CHECK-OBJ-DOLLAR-NEXT:        Kind:            DATA
-# CHECK-OBJ-DOLLAR-NEXT:        Name:            '$dollar_global'
-# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
-# CHECK-OBJ-DOLLAR-NEXT:        Segment:         0
-# CHECK-OBJ-DOLLAR-NEXT:        Size:            4
-# CHECK-OBJ-DOLLAR-NEXT:      - Index:           3
-# CHECK-OBJ-DOLLAR-NEXT:        Kind:            DATA
-# CHECK-OBJ-DOLLAR-NEXT:        Name:            'mid$dollar_global'
-# CHECK-OBJ-DOLLAR-NEXT:        Flags:           [  ]
-# CHECK-OBJ-DOLLAR-NEXT:        Segment:         1
-# CHECK-OBJ-DOLLAR-NEXT:        Size:            4
-
-# CHECK-OBJ-MAIN:    SymbolTable:
-# CHECK-OBJ-MAIN-NEXT:      - Index:           0
-# CHECK-OBJ-MAIN-NEXT:        Kind:            DATA
-# CHECK-OBJ-MAIN-NEXT:        Name:            '$dollar_global'
-# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
-# CHECK-OBJ-MAIN-NEXT:      - Index:           1
-# CHECK-OBJ-MAIN-NEXT:        Kind:            DATA
-# CHECK-OBJ-MAIN-NEXT:        Name:            'mid$dollar_global'
-# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
-# CHECK-OBJ-MAIN-NEXT:      - Index:           2
-# CHECK-OBJ-MAIN-NEXT:        Kind:            FUNCTION
-# CHECK-OBJ-MAIN-NEXT:        Name:            _start
-# CHECK-OBJ-MAIN-NEXT:        Flags:           [  ]
-# CHECK-OBJ-MAIN-NEXT:        Function:        2
-# CHECK-OBJ-MAIN-NEXT:      - Index:           3
-# CHECK-OBJ-MAIN-NEXT:        Kind:            FUNCTION
-# CHECK-OBJ-MAIN-NEXT:        Name:            '$dollar_func'
-# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
-# CHECK-OBJ-MAIN-NEXT:        Function:        0
-# CHECK-OBJ-MAIN-NEXT:      - Index:           4
-# CHECK-OBJ-MAIN-NEXT:        Kind:            FUNCTION
-# CHECK-OBJ-MAIN-NEXT:        Name:            'mid$dollar_func'
-# CHECK-OBJ-MAIN-NEXT:        Flags:           [ UNDEFINED ]
-# CHECK-OBJ-MAIN-NEXT:        Function:        1
-
-# CHECK-OUT:        - Type:            CUSTOM
-# CHECK-OUT-NEXT:        Name:            name
-# CHECK-OUT-NEXT:        FunctionNames:
-# CHECK-OUT-NEXT:          - Index:           0
-# CHECK-OUT-NEXT:            Name:            '$dollar_func'
-# CHECK-OUT-NEXT:          - Index:           1
-# CHECK-OUT-NEXT:            Name:            'mid$dollar_func'
-# CHECK-OUT-NEXT:          - Index:           2
-
-#--- main.s
-
-.functype $dollar_func () -> ()
-.functype mid$dollar_func () -> ()
-
-.globl $dollar_global
-.globl mid$dollar_global
-
-.globl _start
-_start:
-  .functype _start () -> ()
-  call $dollar_func
-  call mid$dollar_func
-  i32.const $dollar_global
-  drop
-  i32.const mid$dollar_global
-  drop
-  end_function
-
-#--- dollar.s
-
-.globl $dollar_func
-$dollar_func:
-  .functype $dollar_func () -> ()
-  end_function
-
-.globl mid$dollar_func
-mid$dollar_func:
-  .functype mid$dollar_func () -> ()
-  end_function
-
-.globl $dollar_global
-.section  ".data.$dollar_global","",@
-$dollar_global:
-  .int32 42
-  .size $dollar_global, 4
-
-.globl mid$dollar_global
-.section  ".data.mid$dollar_global","",@
-mid$dollar_global:
-  .int32 43
-  .size mid$dollar_global, 4
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
index a69f0eb1db196..a783e5fd8521a 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyMCAsmInfo.cpp
@@ -57,7 +57,5 @@ WebAssemblyMCAsmInfo::WebAssemblyMCAsmInfo(const Triple &T,
   SupportsDebugInformation = true;
   ExceptionsType = ExceptionHandling::None;
 
-  AllowDollarAtStartOfIdentifier = true;
-
   initializeAtSpecifiers(atSpecifiers);
 }
diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index 0e7f4614b7d36..5db51bad8c70c 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -25,8 +25,8 @@ using namespace llvm;
 static bool shouldQuoteName(StringRef Name) {
   // Wasm export/import names and import module names can contain characters
   // that are not allowed in identifiers, so we need to quote them.
-  auto isValidStartChar = [](char C) { return isalpha(C) || C == '_' || C == '.' || C == '$'; };
-  auto isValidChar = [&](char C) { return isValidStartChar(C) || isdigit(C); };
+  auto isValidStartChar = [](char C) { return isalpha(C) || C == '_' || C == '.'; };
+  auto isValidChar = [&](char C) { return isValidStartChar(C) || isdigit(C) || C == '$'; };
   return !(Name.size() > 0 && isValidStartChar(Name.front())) || 
         llvm::any_of(Name, [&](char C) { return !isValidChar(C); });
 }
diff --git a/llvm/test/MC/WebAssembly/export-name.s b/llvm/test/MC/WebAssembly/export-name.s
index 23cce3fd762ee..92be384ecf9ec 100644
--- a/llvm/test/MC/WebAssembly/export-name.s
+++ b/llvm/test/MC/WebAssembly/export-name.s
@@ -14,12 +14,6 @@ square:
     .export_name square, "[square]"
     end_function
 
-$dollar:
-    .globl $dollar
-    .functype $dollar () -> ()
-    .export_name $dollar, $dollar
-    end_function
-
 mid$dollar:
     .globl mid$dollar
     .functype mid$dollar () -> ()
@@ -28,7 +22,6 @@ mid$dollar:
 
 # CHECK: .export_name foo, bar
 # CHECK: .export_name square, "[square]"
-# CHECK: .export_name $dollar, $dollar
 # CHECK: .export_name mid$dollar, mid$dollar
 
 # CHECK-OBJ:        - Type:            EXPORT
@@ -52,11 +45,6 @@ mid$dollar:
 # CHECK-OBJ-NEXT:         Function:        1
 # CHECK-OBJ-NEXT:       - Index:           2
 # CHECK-OBJ-NEXT:         Kind:            FUNCTION
-# CHECK-OBJ-NEXT:         Name:            '$dollar'
-# CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
-# CHECK-OBJ-NEXT:         Function:        2
-# CHECK-OBJ-NEXT:       - Index:           3
-# CHECK-OBJ-NEXT:         Kind:            FUNCTION
 # CHECK-OBJ-NEXT:         Name:            'mid$dollar'
 # CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
-# CHECK-OBJ-NEXT:         Function:        3
+# CHECK-OBJ-NEXT:         Function:        2
diff --git a/llvm/test/MC/WebAssembly/import-module.s b/llvm/test/MC/WebAssembly/import-module.s
index 110496fdb399e..f540c393cd118 100644
--- a/llvm/test/MC/WebAssembly/import-module.s
+++ b/llvm/test/MC/WebAssembly/import-module.s
@@ -22,9 +22,6 @@ test:
   .import_module __wasm_component_model_builtin_context_get_0, "$root"
   .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
 
-  .import_module __wasm_component_model_builtin_context_get_1, $root
-  .import_name __wasm_component_model_builtin_context_get_1, "[context-get-1]"
-
 # CHECK-ASM: .import_module  foo, bar
 # CHECK-ASM: .import_name  foo, qux
 
@@ -43,11 +40,6 @@ test:
 # CHECK-NEXT:         Kind:            FUNCTION
 # CHECK-NEXT:         SigIndex:        1
 
-# CHECK:            - Module:          '$root'
-# CHECK-NEXT:         Field:           '[context-get-1]'
-# CHECK-NEXT:         Kind:            FUNCTION
-# CHECK-NEXT:         SigIndex:        1
-
 # CHECK:        - Type:            CUSTOM
 # CHECK:              Name:            foo
 # CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
@@ -57,6 +49,3 @@ test:
 
 # CHECK:              Name:            __wasm_component_model_builtin_context_get_0
 # CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
-
-# CHECK:              Name:            __wasm_component_model_builtin_context_get_1
-# CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
\ No newline at end of file

>From aaf39fff960b31f17cc356931797e51a64b1b24a Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Thu, 26 Feb 2026 08:28:28 +0000
Subject: [PATCH 08/12] Remove more changes

---
 llvm/test/MC/WebAssembly/export-name.s | 12 ------------
 1 file changed, 12 deletions(-)

diff --git a/llvm/test/MC/WebAssembly/export-name.s b/llvm/test/MC/WebAssembly/export-name.s
index 92be384ecf9ec..eb215b68d0be6 100644
--- a/llvm/test/MC/WebAssembly/export-name.s
+++ b/llvm/test/MC/WebAssembly/export-name.s
@@ -14,15 +14,8 @@ square:
     .export_name square, "[square]"
     end_function
 
-mid$dollar:
-    .globl mid$dollar
-    .functype mid$dollar () -> ()
-    .export_name mid$dollar, mid$dollar
-    end_function
-
 # CHECK: .export_name foo, bar
 # CHECK: .export_name square, "[square]"
-# CHECK: .export_name mid$dollar, mid$dollar
 
 # CHECK-OBJ:        - Type:            EXPORT
 # CHECK-OBJ-NEXT:     Exports:
@@ -43,8 +36,3 @@ mid$dollar:
 # CHECK-OBJ-NEXT:         Name:            square
 # CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
 # CHECK-OBJ-NEXT:         Function:        1
-# CHECK-OBJ-NEXT:       - Index:           2
-# CHECK-OBJ-NEXT:         Kind:            FUNCTION
-# CHECK-OBJ-NEXT:         Name:            'mid$dollar'
-# CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
-# CHECK-OBJ-NEXT:         Function:        2

>From a7bc50237483dfca836f23a0652f3e726513f2e0 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Thu, 26 Feb 2026 08:43:02 +0000
Subject: [PATCH 09/12] More testing

---
 .../WebAssemblyTargetStreamer.cpp             | 14 +++++---
 llvm/test/MC/WebAssembly/export-name.s        | 24 +++++++++++++
 llvm/test/MC/WebAssembly/import-module.s      | 34 ++++++++++++++++---
 3 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index 5db51bad8c70c..29158b67820e3 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -25,10 +25,16 @@ using namespace llvm;
 static bool shouldQuoteName(StringRef Name) {
   // Wasm export/import names and import module names can contain characters
   // that are not allowed in identifiers, so we need to quote them.
-  auto isValidStartChar = [](char C) { return isalpha(C) || C == '_' || C == '.'; };
-  auto isValidChar = [&](char C) { return isValidStartChar(C) || isdigit(C) || C == '$'; };
-  return !(Name.size() > 0 && isValidStartChar(Name.front())) || 
-        llvm::any_of(Name, [&](char C) { return !isValidChar(C); });
+  // This logic is based on the expectations of MC/MCParser/AsmLexer as
+  // configured for WebAssembly.
+  auto isValidStartChar = [](char C) {
+    return isalpha(C) || C == '_' || C == '.';
+  };
+  auto isValidChar = [&](char C) {
+    return isValidStartChar(C) || isdigit(C) || C == '$' || C == '?';
+  };
+  return !(Name.size() > 0 && isValidStartChar(Name.front())) ||
+         llvm::any_of(Name, [&](char C) { return !isValidChar(C); });
 }
 
 WebAssemblyTargetStreamer::WebAssemblyTargetStreamer(MCStreamer &S)
diff --git a/llvm/test/MC/WebAssembly/export-name.s b/llvm/test/MC/WebAssembly/export-name.s
index eb215b68d0be6..d9d3818161f06 100644
--- a/llvm/test/MC/WebAssembly/export-name.s
+++ b/llvm/test/MC/WebAssembly/export-name.s
@@ -14,8 +14,22 @@ square:
     .export_name square, "[square]"
     end_function
 
+mid$dollar:
+    .globl mid$dollar
+    .functype mid$dollar () -> ()
+    .export_name mid$dollar, mid$dollar
+    end_function
+
+mid?question:
+    .globl mid?question
+    .functype mid?question () -> ()
+    .export_name mid?question, mid?question
+    end_function
+
 # CHECK: .export_name foo, bar
 # CHECK: .export_name square, "[square]"
+# CHECK: .export_name mid$dollar, mid$dollar
+# CHECK: .export_name mid?question, mid?question
 
 # CHECK-OBJ:        - Type:            EXPORT
 # CHECK-OBJ-NEXT:     Exports:
@@ -36,3 +50,13 @@ square:
 # CHECK-OBJ-NEXT:         Name:            square
 # CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
 # CHECK-OBJ-NEXT:         Function:        1
+# CHECK-OBJ-NEXT:       - Index:           2
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Name:            'mid$dollar'
+# CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
+# CHECK-OBJ-NEXT:         Function:        2
+# CHECK-OBJ-NEXT:       - Index:           3
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Name:            'mid?question'
+# CHECK-OBJ-NEXT:         Flags:           [ EXPORTED ]
+# CHECK-OBJ-NEXT:         Function:        3
diff --git a/llvm/test/MC/WebAssembly/import-module.s b/llvm/test/MC/WebAssembly/import-module.s
index f540c393cd118..7051d72186f5a 100644
--- a/llvm/test/MC/WebAssembly/import-module.s
+++ b/llvm/test/MC/WebAssembly/import-module.s
@@ -4,7 +4,8 @@
 .functype foo () -> ()
 .functype plain () -> ()
 .functype __wasm_component_model_builtin_context_get_0 () -> (i32)
-.functype __wasm_component_model_builtin_context_get_1 () -> (i32)
+.functype mid$dollar () -> ()
+.functype mid?question () -> ()
 
 test:
   .functype test () -> ()
@@ -12,8 +13,8 @@ test:
   call      plain
   call     __wasm_component_model_builtin_context_get_0
   drop
-  call     __wasm_component_model_builtin_context_get_1
-  drop
+  call     mid$dollar
+  call     mid?question
   end_function
 
   .import_module  foo, bar
@@ -22,8 +23,20 @@ test:
   .import_module __wasm_component_model_builtin_context_get_0, "$root"
   .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
 
+  .import_module mid$dollar, another$mid$dollar
+  .import_name mid$dollar, mid$dollar
+
+  .import_module mid?question, another?mid?question
+  .import_name mid?question, mid?question
+
 # CHECK-ASM: .import_module  foo, bar
 # CHECK-ASM: .import_name  foo, qux
+# CHECK-ASM: .import_module __wasm_component_model_builtin_context_get_0, "$root"
+# CHECK-ASM: .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
+# CHECK-ASM: .import_module mid$dollar, another$mid$dollar
+# CHECK-ASM: .import_name mid$dollar, mid$dollar
+# CHECK-ASM: .import_module mid?question, another?mid?question
+# CHECK-ASM: .import_name mid?question, mid?question
 
 # CHECK:        - Type:            IMPORT
 # CHECK-NEXT:     Imports:
@@ -38,7 +51,14 @@ test:
 # CHECK:            - Module:          '$root'
 # CHECK-NEXT:         Field:           '[context-get-0]'
 # CHECK-NEXT:         Kind:            FUNCTION
-# CHECK-NEXT:         SigIndex:        1
+
+# CHECK:            - Module:          'another$mid$dollar'
+# CHECK-NEXT:         Field:           'mid$dollar'
+# CHECK-NEXT:         Kind:            FUNCTION
+
+# CHECK:            - Module:          'another?mid?question'
+# CHECK-NEXT:         Field:           'mid?question'
+# CHECK-NEXT:         Kind:            FUNCTION
 
 # CHECK:        - Type:            CUSTOM
 # CHECK:              Name:            foo
@@ -49,3 +69,9 @@ test:
 
 # CHECK:              Name:            __wasm_component_model_builtin_context_get_0
 # CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
+
+# CHECK:              Name:            'mid$dollar'
+# CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
+
+# CHECK:              Name:            'mid?question'
+# CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]

>From e790f3d7bc208a4818e2fa9dee7754fe420c777b Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Fri, 27 Feb 2026 08:22:53 +0000
Subject: [PATCH 10/12] Simplify changes

---
 .../WebAssemblyTargetStreamer.cpp             | 35 +++----------------
 llvm/test/MC/WebAssembly/export-name.s        | 15 ++++++--
 llvm/test/MC/WebAssembly/import-module.s      | 35 +++++++++----------
 3 files changed, 33 insertions(+), 52 deletions(-)

diff --git a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
index 29158b67820e3..5cff8a9e092cd 100644
--- a/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
+++ b/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyTargetStreamer.cpp
@@ -22,21 +22,6 @@
 #include "llvm/Support/FormattedStream.h"
 using namespace llvm;
 
-static bool shouldQuoteName(StringRef Name) {
-  // Wasm export/import names and import module names can contain characters
-  // that are not allowed in identifiers, so we need to quote them.
-  // This logic is based on the expectations of MC/MCParser/AsmLexer as
-  // configured for WebAssembly.
-  auto isValidStartChar = [](char C) {
-    return isalpha(C) || C == '_' || C == '.';
-  };
-  auto isValidChar = [&](char C) {
-    return isValidStartChar(C) || isdigit(C) || C == '$' || C == '?';
-  };
-  return !(Name.size() > 0 && isValidStartChar(Name.front())) ||
-         llvm::any_of(Name, [&](char C) { return !isValidChar(C); });
-}
-
 WebAssemblyTargetStreamer::WebAssemblyTargetStreamer(MCStreamer &S)
     : MCTargetStreamer(S) {}
 
@@ -109,32 +94,20 @@ void WebAssemblyTargetAsmStreamer::emitTagType(const MCSymbolWasm *Sym) {
   OS << "\n";
 }
 
-static void emitNameWithQuoting(formatted_raw_ostream &OS, StringRef Name) {
-  if (shouldQuoteName(Name))
-    OS << '"' << Name << '"';
-  else
-    OS << Name;
-}
-
 void WebAssemblyTargetAsmStreamer::emitImportModule(const MCSymbolWasm *Sym,
                                                     StringRef ImportModule) {
-  OS << "\t.import_module\t" << Sym->getName() << ", ";
-  emitNameWithQuoting(OS, ImportModule);
-  OS << '\n';
+  OS << "\t.import_module\t" << Sym->getName() << ", \"" << ImportModule
+     << "\"\n";
 }
 
 void WebAssemblyTargetAsmStreamer::emitImportName(const MCSymbolWasm *Sym,
                                                   StringRef ImportName) {
-  OS << "\t.import_name\t" << Sym->getName() << ", ";
-  emitNameWithQuoting(OS, ImportName);
-  OS << '\n';
+  OS << "\t.import_name\t" << Sym->getName() << ", \"" << ImportName << "\"\n";
 }
 
 void WebAssemblyTargetAsmStreamer::emitExportName(const MCSymbolWasm *Sym,
                                                   StringRef ExportName) {
-  OS << "\t.export_name\t" << Sym->getName() << ", ";
-  emitNameWithQuoting(OS, ExportName);
-  OS << '\n';
+  OS << "\t.export_name\t" << Sym->getName() << ", \"" << ExportName << "\"\n";
 }
 
 void WebAssemblyTargetAsmStreamer::emitIndIdx(const MCExpr *Value) {
diff --git a/llvm/test/MC/WebAssembly/export-name.s b/llvm/test/MC/WebAssembly/export-name.s
index d9d3818161f06..29dee04c59f87 100644
--- a/llvm/test/MC/WebAssembly/export-name.s
+++ b/llvm/test/MC/WebAssembly/export-name.s
@@ -26,16 +26,25 @@ mid?question:
     .export_name mid?question, mid?question
     end_function
 
-# CHECK: .export_name foo, bar
+# CHECK: .export_name foo, "bar"
 # CHECK: .export_name square, "[square]"
-# CHECK: .export_name mid$dollar, mid$dollar
-# CHECK: .export_name mid?question, mid?question
+# CHECK: .export_name mid$dollar, "mid$dollar"
+# CHECK: .export_name mid?question, "mid?question"
 
 # CHECK-OBJ:        - Type:            EXPORT
 # CHECK-OBJ-NEXT:     Exports:
 # CHECK-OBJ-NEXT:       - Name:            bar
 # CHECK-OBJ-NEXT:         Kind:            FUNCTION
 # CHECK-OBJ-NEXT:         Index:           0
+# CHECK-OBJ-NEXT:       - Name:            '[square]'
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Index:           1
+# CHECK-OBJ-NEXT:       - Name:            'mid$dollar'
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Index:           2
+# CHECK-OBJ-NEXT:       - Name:            'mid?question'
+# CHECK-OBJ-NEXT:         Kind:            FUNCTION
+# CHECK-OBJ-NEXT:         Index:           3
 
 # CHECK-OBJ:          Name:            linking
 # CHECK-OBJ-NEXT:     Version:         2
diff --git a/llvm/test/MC/WebAssembly/import-module.s b/llvm/test/MC/WebAssembly/import-module.s
index 7051d72186f5a..fb07ed5c2baf0 100644
--- a/llvm/test/MC/WebAssembly/import-module.s
+++ b/llvm/test/MC/WebAssembly/import-module.s
@@ -3,7 +3,7 @@
 
 .functype foo () -> ()
 .functype plain () -> ()
-.functype __wasm_component_model_builtin_context_get_0 () -> (i32)
+.functype utf8_import() -> ()
 .functype mid$dollar () -> ()
 .functype mid?question () -> ()
 
@@ -11,17 +11,16 @@ test:
   .functype test () -> ()
   call      foo
   call      plain
-  call     __wasm_component_model_builtin_context_get_0
-  drop
-  call     mid$dollar
-  call     mid?question
+  call      utf8_import
+  call      mid$dollar
+  call      mid?question
   end_function
 
   .import_module  foo, bar
   .import_name  foo, qux
 
-  .import_module __wasm_component_model_builtin_context_get_0, "$root"
-  .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
+  .import_module utf8_import, "$café-Straßburg"
+  .import_name utf8_import, "[café-Straßburg]"
 
   .import_module mid$dollar, another$mid$dollar
   .import_name mid$dollar, mid$dollar
@@ -29,14 +28,14 @@ test:
   .import_module mid?question, another?mid?question
   .import_name mid?question, mid?question
 
-# CHECK-ASM: .import_module  foo, bar
-# CHECK-ASM: .import_name  foo, qux
-# CHECK-ASM: .import_module __wasm_component_model_builtin_context_get_0, "$root"
-# CHECK-ASM: .import_name __wasm_component_model_builtin_context_get_0, "[context-get-0]"
-# CHECK-ASM: .import_module mid$dollar, another$mid$dollar
-# CHECK-ASM: .import_name mid$dollar, mid$dollar
-# CHECK-ASM: .import_module mid?question, another?mid?question
-# CHECK-ASM: .import_name mid?question, mid?question
+# CHECK-ASM: .import_module  foo, "bar"
+# CHECK-ASM: .import_name  foo, "qux"
+# CHECK-ASM: .import_module utf8_import, "$café-Straßburg"
+# CHECK-ASM: .import_name utf8_import, "[café-Straßburg]"
+# CHECK-ASM: .import_module mid$dollar, "another$mid$dollar"
+# CHECK-ASM: .import_name mid$dollar, "mid$dollar"
+# CHECK-ASM: .import_module mid?question, "another?mid?question"
+# CHECK-ASM: .import_name mid?question, "mid?question"
 
 # CHECK:        - Type:            IMPORT
 # CHECK-NEXT:     Imports:
@@ -48,8 +47,8 @@ test:
 # CHECK-NEXT:         Field:           plain
 # CHECK-NEXT:         Kind:            FUNCTION
 
-# CHECK:            - Module:          '$root'
-# CHECK-NEXT:         Field:           '[context-get-0]'
+# CHECK:            - Module:          "$café-Straßburg"
+# CHECK-NEXT:         Field:           "[café-Straßburg]"
 # CHECK-NEXT:         Kind:            FUNCTION
 
 # CHECK:            - Module:          'another$mid$dollar'
@@ -67,7 +66,7 @@ test:
 # CHECK:              Name:            plain
 # CHECK-NEXT:         Flags:           [ UNDEFINED ]
 
-# CHECK:              Name:            __wasm_component_model_builtin_context_get_0
+# CHECK:              Name:            utf8_import
 # CHECK-NEXT:         Flags:           [ UNDEFINED, EXPLICIT_NAME ]
 
 # CHECK:              Name:            'mid$dollar'

>From 9c226e0e0a469e0c991dc94277014251a6e54c6a Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Fri, 27 Feb 2026 08:51:55 +0000
Subject: [PATCH 11/12] Fix tests

---
 llvm/test/CodeGen/WebAssembly/export-name.ll             | 4 ++--
 llvm/test/CodeGen/WebAssembly/import-module.ll           | 4 ++--
 llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll | 8 ++++----
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/test/CodeGen/WebAssembly/export-name.ll b/llvm/test/CodeGen/WebAssembly/export-name.ll
index 9941520f21918..815f6d3b4ed1e 100644
--- a/llvm/test/CodeGen/WebAssembly/export-name.ll
+++ b/llvm/test/CodeGen/WebAssembly/export-name.ll
@@ -12,5 +12,5 @@ declare void @test2() #1
 attributes #0 = { "wasm-export-name"="foo" }
 attributes #1 = { "wasm-export-name"="bar" }
 
-; CHECK: .export_name test, foo
-; CHECK: .export_name test2, bar
+; CHECK: .export_name test, "foo"
+; CHECK: .export_name test2, "bar"
diff --git a/llvm/test/CodeGen/WebAssembly/import-module.ll b/llvm/test/CodeGen/WebAssembly/import-module.ll
index 1d423fbf5426d..101459f47d482 100644
--- a/llvm/test/CodeGen/WebAssembly/import-module.ll
+++ b/llvm/test/CodeGen/WebAssembly/import-module.ll
@@ -14,6 +14,6 @@ declare void @plain()
 attributes #0 = { "wasm-import-module"="bar" "wasm-import-name"="qux" }
 
 ; CHECK-NOT: .import_module plain
-;     CHECK: .import_module foo, bar
-;     CHECK: .import_name foo, qux
+;     CHECK: .import_module foo, "bar"
+;     CHECK: .import_name foo, "qux"
 ; CHECK-NOT: .import_module plain
diff --git a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll
index 2c4d52937d944..d33b27ef7f327 100644
--- a/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll
+++ b/llvm/test/CodeGen/WebAssembly/lower-em-ehsjlj-options.ll
@@ -5,11 +5,11 @@
 target triple = "wasm32-unknown-unknown"
 
 ; EH: .functype  invoke_vi (i32, i32) -> ()
-; EH: .import_module  invoke_vi, env
-; EH: .import_name  invoke_vi, invoke_vi
+; EH: .import_module  invoke_vi, "env"
+; EH: .import_name  invoke_vi, "invoke_vi"
 ; EH-NOT: .functype  __invoke_void_i32
-; EH-NOT: .import_module  __invoke_void_i32
-; EH-NOT: .import_name  __invoke_void_i32
+; EH-NOT: .import_module  "__invoke_void_i32"
+; EH-NOT: .import_name  "__invoke_void_i32"
 
 ; SJLJ: .functype  emscripten_longjmp (i32, i32) -> ()
 ; SJLJ-NOT: .functype  emscripten_longjmp_jmpbuf

>From 704dffcaf7166192e75044d7892c5f32e1359e51 Mon Sep 17 00:00:00 2001
From: Sy Brand <sy.brand at fastly.com>
Date: Fri, 27 Feb 2026 19:03:52 +0000
Subject: [PATCH 12/12] Change import names for import-module test

---
 llvm/test/MC/WebAssembly/import-module.s | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/test/MC/WebAssembly/import-module.s b/llvm/test/MC/WebAssembly/import-module.s
index fb07ed5c2baf0..7ac29980aaeb5 100644
--- a/llvm/test/MC/WebAssembly/import-module.s
+++ b/llvm/test/MC/WebAssembly/import-module.s
@@ -23,19 +23,19 @@ test:
   .import_name utf8_import, "[café-Straßburg]"
 
   .import_module mid$dollar, another$mid$dollar
-  .import_name mid$dollar, mid$dollar
+  .import_name mid$dollar, mid$dollar$name
 
   .import_module mid?question, another?mid?question
-  .import_name mid?question, mid?question
+  .import_name mid?question, mid?question?name
 
 # CHECK-ASM: .import_module  foo, "bar"
 # CHECK-ASM: .import_name  foo, "qux"
 # CHECK-ASM: .import_module utf8_import, "$café-Straßburg"
 # CHECK-ASM: .import_name utf8_import, "[café-Straßburg]"
 # CHECK-ASM: .import_module mid$dollar, "another$mid$dollar"
-# CHECK-ASM: .import_name mid$dollar, "mid$dollar"
+# CHECK-ASM: .import_name mid$dollar, "mid$dollar$name"
 # CHECK-ASM: .import_module mid?question, "another?mid?question"
-# CHECK-ASM: .import_name mid?question, "mid?question"
+# CHECK-ASM: .import_name mid?question, "mid?question?name"
 
 # CHECK:        - Type:            IMPORT
 # CHECK-NEXT:     Imports:
@@ -52,11 +52,11 @@ test:
 # CHECK-NEXT:         Kind:            FUNCTION
 
 # CHECK:            - Module:          'another$mid$dollar'
-# CHECK-NEXT:         Field:           'mid$dollar'
+# CHECK-NEXT:         Field:           'mid$dollar$name'
 # CHECK-NEXT:         Kind:            FUNCTION
 
 # CHECK:            - Module:          'another?mid?question'
-# CHECK-NEXT:         Field:           'mid?question'
+# CHECK-NEXT:         Field:           'mid?question?name'
 # CHECK-NEXT:         Kind:            FUNCTION
 
 # CHECK:        - Type:            CUSTOM



More information about the llvm-commits mailing list