Skip to content

Partial support for common linkage for WebAssembly #151478

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

serge-sans-paille
Copy link
Collaborator

Emulate common linkage through weak linkage, the only difference between the two being mostly similar according to langref, with the notable exception that common linkage implies zero initializer.

This is needed for fortran-to-web-assembly projects used in the python community, esp.

https://github.com/emscripten-forge/recipes/tree/main/recipes/recipes_emscripten/libflang

@llvmbot llvmbot added backend:WebAssembly mc Machine (object) code labels Jul 31, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 31, 2025

@llvm/pr-subscribers-mc

@llvm/pr-subscribers-backend-webassembly

Author: None (serge-sans-paille)

Changes

Emulate common linkage through weak linkage, the only difference between the two being mostly similar according to langref, with the notable exception that common linkage implies zero initializer.

This is needed for fortran-to-web-assembly projects used in the python community, esp.

https://github.com/emscripten-forge/recipes/tree/main/recipes/recipes_emscripten/libflang


Full diff: https://github.com/llvm/llvm-project/pull/151478.diff

2 Files Affected:

  • (modified) llvm/lib/MC/MCWasmStreamer.cpp (+24-1)
  • (added) llvm/test/MC/WebAssembly/common.ll (+69)
diff --git a/llvm/lib/MC/MCWasmStreamer.cpp b/llvm/lib/MC/MCWasmStreamer.cpp
index e3ef1117b4125..c5249db753ec6 100644
--- a/llvm/lib/MC/MCWasmStreamer.cpp
+++ b/llvm/lib/MC/MCWasmStreamer.cpp
@@ -14,6 +14,7 @@
 #include "llvm/MC/MCAsmBackend.h"
 #include "llvm/MC/MCAssembler.h"
 #include "llvm/MC/MCCodeEmitter.h"
+#include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCExpr.h"
 #include "llvm/MC/MCFixup.h"
 #include "llvm/MC/MCObjectStreamer.h"
@@ -131,7 +132,29 @@ bool MCWasmStreamer::emitSymbolAttribute(MCSymbol *S, MCSymbolAttr Attribute) {
 
 void MCWasmStreamer::emitCommonSymbol(MCSymbol *S, uint64_t Size,
                                       Align ByteAlignment) {
-  llvm_unreachable("Common symbols are not yet implemented for Wasm");
+  auto *Symbol = cast<MCSymbolWasm>(S);
+
+  pushSection();
+
+  // Common symbols are very close to weak symbols, so manually build a weak
+  // symbol.
+  MCSectionWasm *SW = getContext().getWasmSection(".bss.common." + S->getName(),
+                                                  SectionKind::getData());
+  SW->setAlignment(ByteAlignment);
+  getAssembler().registerSection(*SW);
+
+  switchSection(SW);
+
+  MCDataFragment *DF = getOrCreateDataFragment();
+  DF->setContents(std::vector<char>(Size, '\0'));
+  Symbol->setFragment(DF);
+
+  Symbol->setSize(MCConstantExpr::create(Size, getContext(), false, 8));
+  Symbol->setWeak(true);
+  Symbol->setExternal(true);
+  getAssembler().registerSymbol(*Symbol);
+
+  popSection();
 }
 
 void MCWasmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) {
diff --git a/llvm/test/MC/WebAssembly/common.ll b/llvm/test/MC/WebAssembly/common.ll
new file mode 100644
index 0000000000000..50e7298684ccb
--- /dev/null
+++ b/llvm/test/MC/WebAssembly/common.ll
@@ -0,0 +1,69 @@
+; RUN; llc -mcpu=mvp -filetype=obj %s -o - | obj2yaml | FileCheck %s
+; RUN; llc -mcpu=mvp -filetype=asm %s -asm-verbose=false -o -  | FileCheck --check-prefix=ASM %s
+; RUN: llc -mcpu=mvp -filetype=asm %s -o - | llvm-mc -triple=wasm32 -filetype=obj  -o - | obj2yaml | FileCheck %s
+
+target triple = "wasm32-unknown-unknown"
+;target triple = "x86_64-redhat-linux-gnu"
+@b = common dso_local global [10 x i32] zeroinitializer, align 4
+@c = common dso_local global [20 x i32] zeroinitializer, align 32
+
+; CHECK-ASM: .file	"common.ll"
+; CHECK-ASM: .type	b,@object
+; CHECK-ASM: .comm	b,40,2
+; CHECK-ASM: .type	c,@object
+; CHECK-ASM: .comm	c,80,5
+
+
+; CHECK:      --- !WASM
+; CHECK-NEXT: FileHeader:
+; CHECK-NEXT:   Version:         0x1
+; CHECK-NEXT: Sections:
+; CHECK-NEXT:   - Type:            IMPORT
+; CHECK-NEXT:     Imports:
+; CHECK-NEXT:       - Module:          env
+; CHECK-NEXT:         Field:           __linear_memory
+; CHECK-NEXT:         Kind:            MEMORY
+; CHECK-NEXT:         Memory:
+; CHECK-NEXT:           Minimum:         0x1
+; CHECK-NEXT:   - Type:            DATACOUNT
+; CHECK-NEXT:     Count:           2
+; CHECK-NEXT:   - Type:            DATA
+; CHECK-NEXT:     Segments:
+; CHECK-NEXT:       - SectionOffset:   6
+; CHECK-NEXT:         InitFlags:       0
+; CHECK-NEXT:         Offset:
+; CHECK-NEXT:           Opcode:          I32_CONST
+; CHECK-NEXT:           Value:           0
+; CHECK-NEXT:         Content:         '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
+; CHECK-NEXT:       - SectionOffset:   52
+; CHECK-NEXT:         InitFlags:       0
+; CHECK-NEXT:         Offset:
+; CHECK-NEXT:           Opcode:          I32_CONST
+; CHECK-NEXT:           Value:           64
+; CHECK-NEXT:         Content:         '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+; CHECK-NEXT:   - Type:            CUSTOM
+; CHECK-NEXT:     Name:            linking
+; CHECK-NEXT:     Version:         2
+; CHECK-NEXT:     SymbolTable:
+; CHECK-NEXT:       - Index:           0
+; CHECK-NEXT:         Kind:            DATA
+; CHECK-NEXT:         Name:            b
+; CHECK-NEXT:         Flags:           [ BINDING_WEAK ]
+; CHECK-NEXT:         Segment:         0
+; CHECK-NEXT:         Size:            40
+; CHECK-NEXT:       - Index:           1
+; CHECK-NEXT:         Kind:            DATA
+; CHECK-NEXT:         Name:            c
+; CHECK-NEXT:         Flags:           [ BINDING_WEAK ]
+; CHECK-NEXT:         Segment:         1
+; CHECK-NEXT:         Size:            80
+; CHECK-NEXT:     SegmentInfo:
+; CHECK-NEXT:       - Index:           0
+; CHECK-NEXT:         Name:            .bss.common.b
+; CHECK-NEXT:         Alignment:       2
+; CHECK-NEXT:         Flags:           [  ]
+; CHECK-NEXT:       - Index:           1
+; CHECK-NEXT:         Name:            .bss.common.c
+; CHECK-NEXT:         Alignment:       5
+; CHECK-NEXT:         Flags:           [  ]
+; CHECK-NEXT: ...

Emulate common linkage through weak linkage, the only difference between
the two being mostly similar according to langref, with the notable
exception that common linkage implies zero initializer.

This is needed for fortran-to-web-assembly projects used in the python
community, esp.

https://github.com/emscripten-forge/recipes/tree/main/recipes/recipes_emscripten/libflang
@serge-sans-paille
Copy link
Collaborator Author

@MaskRay I'm new to MC code, don't hesitate to propose a better approach!

Copy link
Member

@MaskRay MaskRay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's 2025, and I suggest that we don't implement COMMON symbols in new object file formats. https://maskray.me/blog/2022-02-06-all-about-common-symbols

GCC since 10 and Clang since 11 default to -fno-common.

In ELF, for a definition in a relocatable object file, the precedence is STB_GLOBAL > COMMON > STB_WEAK.

Using weak isn't super appropriate.

@SylvainCorlay
Copy link

LAPACK and other key scientific codebases make use of common variables.

If we want Flang to be able to build SciPy, or GNU Octave, or other such tools to WebAssembly, having some support for them will be required unfortunately.

@certik
Copy link

certik commented Aug 1, 2025

@MaskRay yes, common is old and should not be used for new code. However, there is still a lot of software in production that uses common. If common symbols should not be used in LLVM anymore, what do you recommend Fortran compilers to emit instead? This applies to both Flang and LFortran.

@MaskRay
Copy link
Member

MaskRay commented Aug 2, 2025

I assume that the Fortran compiler that emits .comm wants to target WebAssembly. Do you need COMMON symbol's deduplication feature? The compiler can emit a COMDAT section group instead. https://maskray.me/blog/2021-07-25-comdat-and-section-group

@SylvainCorlay
Copy link

The compiler can emit a COMDAT section group instead.

That would require changing the compilers (Flang, LFortran), which currently use common linkage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:WebAssembly mc Machine (object) code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants