Skip to content

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 llvm: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.

@dschuff
Copy link
Member

dschuff commented Aug 6, 2025

There is also still plenty of C code out there that depends on -fcommon to compile. including a surprisingly large chunk of the LLVM testsuite that we just can't easily compile to wasm today.

@MaskRay
Copy link
Member

MaskRay commented Aug 17, 2025

The COMMON symbol in COFF, Mach-O, and ELF formats shares a key property: the linker allocates sufficient space to accommodate the largest size required. In contrast, a weak symbol fallback does not meet this requirement. This distinction raises concerns about introducing another method to generate weak symbols, potentially causing confusion.
To address this, you should modify LLVM API users to avoid relying on the common symbol and update any software intended for WebAssembly to disable the -fcommon flag.

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.

NAK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:WebAssembly llvm:mc Machine (object) code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants