Skip to content

Conversation

@andykaylor
Copy link
Contributor

Add support for marking global variables with common linkage.

Add support for marking global variables with common linkage.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Nov 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Nov 18, 2025

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

Changes

Add support for marking global variables with common linkage.


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

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+12-5)
  • (added) clang/test/CIR/CodeGen/no-common.c (+103)
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 4a82ea3121b60..b8e51f87d4045 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -883,8 +883,17 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
   // FIXME(cir): setLinkage should likely set MLIR's visibility automatically.
   gv.setVisibility(getMLIRVisibilityFromCIRLinkage(linkage));
   assert(!cir::MissingFeatures::opGlobalDLLImportExport());
-  if (linkage == cir::GlobalLinkageKind::CommonLinkage)
-    errorNYI(initExpr->getSourceRange(), "common linkage");
+  if (linkage == cir::GlobalLinkageKind::CommonLinkage) {
+    // common vars aren't constant even if declared const.
+    gv.setConstant(false);
+    // Tentative definition of global variables may be initialized with
+    // non-zero null pointers. In this case they should have weak linkage
+    // since common linkage must have zero initializer and must not have
+    // explicit section therefore cannot have non-zero initial value.
+    std::optional<mlir::Attribute> initializer = gv.getInitialValue();
+    if (initializer && !getBuilder().isNullValue(*initializer))
+      gv.setLinkage(cir::GlobalLinkageKind::WeakAnyLinkage);
+  }
 
   setNonAliasAttributes(vd, gv);
 
@@ -1238,10 +1247,8 @@ cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator(
   // linkage.
   if (!getLangOpts().CPlusPlus && isa<VarDecl>(dd) &&
       !isVarDeclStrongDefinition(astContext, *this, cast<VarDecl>(dd),
-                                 getCodeGenOpts().NoCommon)) {
-    errorNYI(dd->getBeginLoc(), "common linkage", dd->getDeclKindName());
+                                 getCodeGenOpts().NoCommon))
     return cir::GlobalLinkageKind::CommonLinkage;
-  }
 
   // selectany symbols are externally visible, so use weak instead of
   // linkonce.  MSVC optimizes away references to const selectany globals, so
diff --git a/clang/test/CIR/CodeGen/no-common.c b/clang/test/CIR/CodeGen/no-common.c
new file mode 100644
index 0000000000000..ce4c5fc0b3a33
--- /dev/null
+++ b/clang/test/CIR/CodeGen/no-common.c
@@ -0,0 +1,103 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -emit-cir -o %t-default.cir
+// RUN: FileCheck --input-file=%t-default.cir %s -check-prefix=CIR-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fno-common -emit-cir -o %t-no-common.cir
+// RUN: FileCheck --input-file=%t-no-common.cir %s -check-prefix=CIR-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fcommon -emit-cir -o %t-common.cir
+// RUN: FileCheck --input-file=%t-common.cir %s -check-prefix=CIR-COMMON
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -emit-llvm -o %t-default-cir.ll
+// RUN: FileCheck --input-file=%t-default-cir.ll %s -check-prefix=LLVM-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fno-common -emit-llvm -o %t-no-common-cir.ll
+// RUN: FileCheck --input-file=%t-no-common-cir.ll %s -check-prefix=LLVM-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fcommon -emit-llvm -o %t-common-cir.ll
+// RUN: FileCheck --input-file=%t-common-cir.ll %s -check-prefix=LLVM-COMMON
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o %t-default.ll
+// RUN: FileCheck --input-file=%t-default.ll %s -check-prefix=OGCG-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fno-common -emit-llvm -o %t-no-common.ll
+// RUN: FileCheck --input-file=%t-no-common.ll %s -check-prefix=OGCG-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fcommon -emit-llvm -o %t-common.ll
+// RUN: FileCheck --input-file=%t-common.ll %s -check-prefix=OGCG-COMMON
+
+const int a = 42;
+// CIR-DEFAULT: cir.global constant external @a = #cir.int<42>
+// LLVM-DEFAULT: @a = constant i32 42
+// OGCG-DEFAULT: @a = constant i32 42
+
+// CIR-COMMON: cir.global constant external @a
+// LLVM-COMMON: @a = constant i32 42
+// OGCG-COMMON: @a = constant i32 42
+
+const int b __attribute__((common)) = 42;
+// CIR-DEFAULT: cir.global constant external @b = #cir.int<42>
+// LLVM-DEFAULT: @b = constant i32 42
+// OGCG-DEFAULT: @b = constant i32 42
+
+// CIR-COMMON: cir.global constant external @b = #cir.int<42>
+// LLVM-COMMON: @b = constant i32 42
+// OGCG-COMMON: @b = constant i32 42
+
+const int c __attribute__((nocommon)) = 42;
+// CIR-DEFAULT: cir.global constant external @c = #cir.int<42>
+// LLVM-DEFAULT: @c = constant i32 42
+// OGCG-DEFAULT: @c = constant i32 42
+
+// CIR-COMMON: cir.global constant external @c = #cir.int<42>
+// LLVM-COMMON: @c = constant i32 42
+// OGCG-COMMON: @c = constant i32 42
+
+int d = 11;
+// CIR-DEFAULT: cir.global external @d = #cir.int<11>
+// LLVM-DEFAULT: @d = global i32 11
+// OGCG-DEFAULT: @d = global i32 11
+
+// CIR-COMMON: cir.global external @d = #cir.int<11>
+// LLVM-COMMON: @d = global i32 11
+// OGCG-COMMON: @d = global i32 11
+
+int e;
+// CIR-DEFAULT: cir.global external @e = #cir.int<0>
+// LLVM-DEFAULT: @e = global i32 0
+// OGCG-DEFAULT: @e = global i32 0
+
+// CIR-COMMON: cir.global common @e = #cir.int<0>
+// LLVM-COMMON: @e = common global i32 0
+// OGCG-COMMON: @e = common global i32 0
+
+
+int f __attribute__((common));
+// CIR-DEFAULT: cir.global common @f = #cir.int<0>
+// LLVM-DEFAULT: @f = common global i32 0
+// OGCG-DEFAULT: @f = common global i32 0
+
+// CIR-COMMON: cir.global common @f
+// LLVM-COMMON: @f = common global i32 0
+// OGCG-COMMON: @f = common global i32 0
+
+int g __attribute__((nocommon));
+// CIR-DEFAULT: cir.global external @g = #cir.int<0>
+// LLVM-DEFAULT: @g = global i32
+// OGCG-DEFAULT: @g = global i32 0
+
+// CIR-COMMON: cir.global external @g = #cir.int<0>
+// LLVM-COMMON: @g = global i32 0
+// OGCG-COMMON: @g = global i32 0
+
+const int h;
+// CIR-DEFAULT: cir.global constant external @h = #cir.int<0>
+// LLVM-DEFAULT: @h = constant i32
+// OGCG-DEFAULT: @h = constant i32 0
+
+// CIR-COMMON: cir.global common @h = #cir.int<0>
+// LLVM-COMMON: @h = common global i32 0
+// OGCG-COMMON: @h = common global i32 0
+
+typedef void* (*fn_t)(long a, long b, char *f, int c);
+fn_t ABC __attribute__ ((nocommon));
+// CIR-DEFAULT: cir.global external @ABC = #cir.ptr<null>
+// LLVM-DEFAULT: @ABC = global ptr null
+// OGCG-DEFAULT: @ABC = global ptr null
+
+// CIR-COMMON: cir.global external @ABC = #cir.ptr<null>
+// LLVM-COMMON: @ABC = global ptr null
+// OGCG-COMMON: @ABC = global ptr null

@llvmbot
Copy link
Member

llvmbot commented Nov 18, 2025

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

Changes

Add support for marking global variables with common linkage.


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

2 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+12-5)
  • (added) clang/test/CIR/CodeGen/no-common.c (+103)
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 4a82ea3121b60..b8e51f87d4045 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -883,8 +883,17 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
   // FIXME(cir): setLinkage should likely set MLIR's visibility automatically.
   gv.setVisibility(getMLIRVisibilityFromCIRLinkage(linkage));
   assert(!cir::MissingFeatures::opGlobalDLLImportExport());
-  if (linkage == cir::GlobalLinkageKind::CommonLinkage)
-    errorNYI(initExpr->getSourceRange(), "common linkage");
+  if (linkage == cir::GlobalLinkageKind::CommonLinkage) {
+    // common vars aren't constant even if declared const.
+    gv.setConstant(false);
+    // Tentative definition of global variables may be initialized with
+    // non-zero null pointers. In this case they should have weak linkage
+    // since common linkage must have zero initializer and must not have
+    // explicit section therefore cannot have non-zero initial value.
+    std::optional<mlir::Attribute> initializer = gv.getInitialValue();
+    if (initializer && !getBuilder().isNullValue(*initializer))
+      gv.setLinkage(cir::GlobalLinkageKind::WeakAnyLinkage);
+  }
 
   setNonAliasAttributes(vd, gv);
 
@@ -1238,10 +1247,8 @@ cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator(
   // linkage.
   if (!getLangOpts().CPlusPlus && isa<VarDecl>(dd) &&
       !isVarDeclStrongDefinition(astContext, *this, cast<VarDecl>(dd),
-                                 getCodeGenOpts().NoCommon)) {
-    errorNYI(dd->getBeginLoc(), "common linkage", dd->getDeclKindName());
+                                 getCodeGenOpts().NoCommon))
     return cir::GlobalLinkageKind::CommonLinkage;
-  }
 
   // selectany symbols are externally visible, so use weak instead of
   // linkonce.  MSVC optimizes away references to const selectany globals, so
diff --git a/clang/test/CIR/CodeGen/no-common.c b/clang/test/CIR/CodeGen/no-common.c
new file mode 100644
index 0000000000000..ce4c5fc0b3a33
--- /dev/null
+++ b/clang/test/CIR/CodeGen/no-common.c
@@ -0,0 +1,103 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -emit-cir -o %t-default.cir
+// RUN: FileCheck --input-file=%t-default.cir %s -check-prefix=CIR-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fno-common -emit-cir -o %t-no-common.cir
+// RUN: FileCheck --input-file=%t-no-common.cir %s -check-prefix=CIR-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fcommon -emit-cir -o %t-common.cir
+// RUN: FileCheck --input-file=%t-common.cir %s -check-prefix=CIR-COMMON
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -emit-llvm -o %t-default-cir.ll
+// RUN: FileCheck --input-file=%t-default-cir.ll %s -check-prefix=LLVM-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fno-common -emit-llvm -o %t-no-common-cir.ll
+// RUN: FileCheck --input-file=%t-no-common-cir.ll %s -check-prefix=LLVM-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir %s -fcommon -emit-llvm -o %t-common-cir.ll
+// RUN: FileCheck --input-file=%t-common-cir.ll %s -check-prefix=LLVM-COMMON
+
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o %t-default.ll
+// RUN: FileCheck --input-file=%t-default.ll %s -check-prefix=OGCG-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fno-common -emit-llvm -o %t-no-common.ll
+// RUN: FileCheck --input-file=%t-no-common.ll %s -check-prefix=OGCG-DEFAULT
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fcommon -emit-llvm -o %t-common.ll
+// RUN: FileCheck --input-file=%t-common.ll %s -check-prefix=OGCG-COMMON
+
+const int a = 42;
+// CIR-DEFAULT: cir.global constant external @a = #cir.int<42>
+// LLVM-DEFAULT: @a = constant i32 42
+// OGCG-DEFAULT: @a = constant i32 42
+
+// CIR-COMMON: cir.global constant external @a
+// LLVM-COMMON: @a = constant i32 42
+// OGCG-COMMON: @a = constant i32 42
+
+const int b __attribute__((common)) = 42;
+// CIR-DEFAULT: cir.global constant external @b = #cir.int<42>
+// LLVM-DEFAULT: @b = constant i32 42
+// OGCG-DEFAULT: @b = constant i32 42
+
+// CIR-COMMON: cir.global constant external @b = #cir.int<42>
+// LLVM-COMMON: @b = constant i32 42
+// OGCG-COMMON: @b = constant i32 42
+
+const int c __attribute__((nocommon)) = 42;
+// CIR-DEFAULT: cir.global constant external @c = #cir.int<42>
+// LLVM-DEFAULT: @c = constant i32 42
+// OGCG-DEFAULT: @c = constant i32 42
+
+// CIR-COMMON: cir.global constant external @c = #cir.int<42>
+// LLVM-COMMON: @c = constant i32 42
+// OGCG-COMMON: @c = constant i32 42
+
+int d = 11;
+// CIR-DEFAULT: cir.global external @d = #cir.int<11>
+// LLVM-DEFAULT: @d = global i32 11
+// OGCG-DEFAULT: @d = global i32 11
+
+// CIR-COMMON: cir.global external @d = #cir.int<11>
+// LLVM-COMMON: @d = global i32 11
+// OGCG-COMMON: @d = global i32 11
+
+int e;
+// CIR-DEFAULT: cir.global external @e = #cir.int<0>
+// LLVM-DEFAULT: @e = global i32 0
+// OGCG-DEFAULT: @e = global i32 0
+
+// CIR-COMMON: cir.global common @e = #cir.int<0>
+// LLVM-COMMON: @e = common global i32 0
+// OGCG-COMMON: @e = common global i32 0
+
+
+int f __attribute__((common));
+// CIR-DEFAULT: cir.global common @f = #cir.int<0>
+// LLVM-DEFAULT: @f = common global i32 0
+// OGCG-DEFAULT: @f = common global i32 0
+
+// CIR-COMMON: cir.global common @f
+// LLVM-COMMON: @f = common global i32 0
+// OGCG-COMMON: @f = common global i32 0
+
+int g __attribute__((nocommon));
+// CIR-DEFAULT: cir.global external @g = #cir.int<0>
+// LLVM-DEFAULT: @g = global i32
+// OGCG-DEFAULT: @g = global i32 0
+
+// CIR-COMMON: cir.global external @g = #cir.int<0>
+// LLVM-COMMON: @g = global i32 0
+// OGCG-COMMON: @g = global i32 0
+
+const int h;
+// CIR-DEFAULT: cir.global constant external @h = #cir.int<0>
+// LLVM-DEFAULT: @h = constant i32
+// OGCG-DEFAULT: @h = constant i32 0
+
+// CIR-COMMON: cir.global common @h = #cir.int<0>
+// LLVM-COMMON: @h = common global i32 0
+// OGCG-COMMON: @h = common global i32 0
+
+typedef void* (*fn_t)(long a, long b, char *f, int c);
+fn_t ABC __attribute__ ((nocommon));
+// CIR-DEFAULT: cir.global external @ABC = #cir.ptr<null>
+// LLVM-DEFAULT: @ABC = global ptr null
+// OGCG-DEFAULT: @ABC = global ptr null
+
+// CIR-COMMON: cir.global external @ABC = #cir.ptr<null>
+// LLVM-COMMON: @ABC = global ptr null
+// OGCG-COMMON: @ABC = global ptr null

// explicit section therefore cannot have non-zero initial value.
std::optional<mlir::Attribute> initializer = gv.getInitialValue();
if (initializer && !getBuilder().isNullValue(*initializer))
gv.setLinkage(cir::GlobalLinkageKind::WeakAnyLinkage);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only test case I could find that reaches this requires OpenCL and AMDGCN support, but I think the code is straightforward enough to go in ahead of a test case.

@andykaylor andykaylor merged commit 3e499e9 into llvm:main Nov 18, 2025
11 of 12 checks passed
@andykaylor andykaylor deleted the cir-common-linkage branch November 18, 2025 22:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants