Skip to content

[CIR] Upstream zero init for global variables #133100

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

Merged
merged 2 commits into from
Mar 31, 2025

Conversation

AmrDeveloper
Copy link
Member

This change adds zero initialization for global variables

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Mar 26, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 26, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Amr Hesham (AmrDeveloper)

Changes

This change adds zero initialization for global variables


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

7 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+34)
  • (modified) clang/lib/CIR/CodeGen/CIRGenModule.cpp (+5-2)
  • (modified) clang/test/CIR/CodeGen/array.cpp (+4-4)
  • (modified) clang/test/CIR/Lowering/array.cpp (+4-4)
  • (modified) clang/test/CIR/Lowering/global-var-simple.cpp (+28-30)
  • (modified) clang/test/CIR/Lowering/hello.c (+1-1)
  • (modified) clang/test/CIR/global-var-simple.cpp (+26-26)
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index ac7658276ec37..eb07acb16ed50 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -67,6 +67,40 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return create<cir::ConstantOp>(loc, attr.getType(), attr);
   }
 
+  mlir::TypedAttr getConstNullPtrAttr(mlir::Type t) {
+    assert(mlir::isa<cir::PointerType>(t) && "expected cir.ptr");
+    return getConstPtrAttr(t, 0);
+  }
+
+  mlir::TypedAttr getZeroAttr(mlir::Type t) {
+    return cir::ZeroAttr::get(getContext(), t);
+  }
+
+  mlir::TypedAttr getZeroInitAttr(mlir::Type ty) {
+    if (mlir::isa<cir::IntType>(ty))
+      return cir::IntAttr::get(ty, 0);
+    if (auto fltType = mlir::dyn_cast<cir::SingleType>(ty))
+      return cir::FPAttr::getZero(fltType);
+    if (auto fltType = mlir::dyn_cast<cir::DoubleType>(ty))
+      return cir::FPAttr::getZero(fltType);
+    if (auto fltType = mlir::dyn_cast<cir::LongDoubleType>(ty))
+      return cir::FPAttr::getZero(fltType);
+    if (auto fltType = mlir::dyn_cast<cir::FP16Type>(ty))
+      return cir::FPAttr::getZero(fltType);
+    if (auto fltType = mlir::dyn_cast<cir::FP128Type>(ty))
+      return cir::FPAttr::getZero(fltType);
+    if (auto fltType = mlir::dyn_cast<cir::BF16Type>(ty))
+      return cir::FPAttr::getZero(fltType);
+    if (auto arrTy = mlir::dyn_cast<cir::ArrayType>(ty))
+      return getZeroAttr(arrTy);
+    if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(ty))
+      return getConstNullPtrAttr(ptrTy);
+    if (mlir::isa<cir::BoolType>(ty)) {
+      return getCIRBoolAttr(false);
+    }
+    llvm_unreachable("Zero initializer for given type is NYI");
+  }
+
   cir::ConstantOp getBool(bool state, mlir::Location loc) {
     return create<cir::ConstantOp>(loc, getBoolTy(), getCIRBoolAttr(state));
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 9776a4e09f9e0..2a37d6c7d1888 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -140,17 +140,20 @@ void CIRGenModule::emitGlobalVarDefinition(const clang::VarDecl *vd,
     // certain constant expressions is implemented for now.
     const VarDecl *initDecl;
     const Expr *initExpr = vd->getAnyInitializer(initDecl);
+    mlir::Attribute initializer;
     if (initExpr) {
-      mlir::Attribute initializer;
       if (APValue *value = initDecl->evaluateValue()) {
         ConstantEmitter emitter(*this);
         initializer = emitter.tryEmitPrivateForMemory(*value, astTy);
       } else {
         errorNYI(initExpr->getSourceRange(), "non-constant initializer");
       }
-      varOp.setInitialValueAttr(initializer);
+    } else {
+      initializer = builder.getZeroInitAttr(convertType(astTy));
     }
 
+    varOp.setInitialValueAttr(initializer);
+
     // Set CIR's linkage type as appropriate.
     cir::GlobalLinkageKind linkage =
         getCIRLinkageVarDefinition(vd, /*IsConstant=*/false);
diff --git a/clang/test/CIR/CodeGen/array.cpp b/clang/test/CIR/CodeGen/array.cpp
index a59880352e050..1e74275eab058 100644
--- a/clang/test/CIR/CodeGen/array.cpp
+++ b/clang/test/CIR/CodeGen/array.cpp
@@ -1,16 +1,16 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
 
 int a[10];
-// CHECK: cir.global external @a : !cir.array<!s32i x 10>
+// CHECK: cir.global external @a = #cir.zero : !cir.array<!s32i x 10>
 
 int aa[10][5];
-// CHECK: cir.global external @aa : !cir.array<!cir.array<!s32i x 5> x 10>
+// CHECK: cir.global external @aa = #cir.zero : !cir.array<!cir.array<!s32i x 5> x 10>
 
 extern int b[10];
-// CHECK: cir.global external @b : !cir.array<!s32i x 10>
+// CHECK: cir.global external @b = #cir.zero : !cir.array<!s32i x 10>
 
 extern int bb[10][5];
-// CHECK: cir.global external @bb : !cir.array<!cir.array<!s32i x 5> x 10>
+// CHECK: cir.global external @bb = #cir.zero : !cir.array<!cir.array<!s32i x 5> x 10>
 
 int c[10] = {};
 // CHECK: cir.global external @c = #cir.zero : !cir.array<!s32i x 10>
diff --git a/clang/test/CIR/Lowering/array.cpp b/clang/test/CIR/Lowering/array.cpp
index 763980b9124a3..4fb996aefe79e 100644
--- a/clang/test/CIR/Lowering/array.cpp
+++ b/clang/test/CIR/Lowering/array.cpp
@@ -1,16 +1,16 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s
 
 int a[10];
-// CHECK: @a = external dso_local global [10 x i32]
+// CHECK: @a = dso_local global [10 x i32] zeroinitializer
 
 int aa[10][5];
-// CHECK: @aa = external dso_local global [10 x [5 x i32]]
+// CHECK: @aa = dso_local global [10 x [5 x i32]] zeroinitializer
 
 extern int b[10];
-// CHECK: @b = external dso_local global [10 x i32]
+// CHECK: @b = dso_local global [10 x i32] zeroinitializer
 
 extern int bb[10][5];
-// CHECK: @bb = external dso_local global [10 x [5 x i32]]
+// CHECK: @bb = dso_local global [10 x [5 x i32]] zeroinitializer
 
 int c[10] = {};
 // CHECK: @c = dso_local global [10 x i32] zeroinitializer
diff --git a/clang/test/CIR/Lowering/global-var-simple.cpp b/clang/test/CIR/Lowering/global-var-simple.cpp
index ab8c6660a311b..33b418430d478 100644
--- a/clang/test/CIR/Lowering/global-var-simple.cpp
+++ b/clang/test/CIR/Lowering/global-var-simple.cpp
@@ -1,21 +1,19 @@
 // Global variables of intergal types
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o -  | FileCheck %s
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s
 
-// Note: Currently unsupported features include default zero-initialization
-//       and alignment. The fact that "external" is only printed for globals
-//       without an initializer is a quirk of the LLVM AsmWriter.
+// Note: Currently unsupported features include alignment..
 
 char c;
-// CHECK: @c = external dso_local global i8
+// CHECK: @c = dso_local global i8 0
 
 signed char sc;
-// CHECK: @sc = external dso_local global i8
+// CHECK: @sc = dso_local global i8 0
 
 unsigned char uc;
-// CHECK: @uc = external dso_local global i8
+// CHECK: @uc = dso_local global i8 0
 
 short ss;
-// CHECK: @ss = external dso_local global i16
+// CHECK: @ss = dso_local global i16 0
 
 unsigned short us = 100;
 // CHECK: @us = dso_local global i16 100
@@ -24,82 +22,82 @@ int si = 42;
 // CHECK: @si = dso_local global i32 42
 
 unsigned ui;
-// CHECK: @ui = external dso_local global i32
+// CHECK: @ui = dso_local global i32 0
 
 long sl;
-// CHECK: @sl = external dso_local global i64
+// CHECK: @sl = dso_local global i64 0
 
 unsigned long ul;
-// CHECK: @ul = external dso_local global i64
+// CHECK: @ul = dso_local global i64 0
 
 long long sll;
-// CHECK: @sll = external dso_local global i64
+// CHECK: @sll = dso_local global i64 0
 
 unsigned long long ull = 123456;
 // CHECK: @ull = dso_local global i64 123456
 
 __int128 s128;
-// CHECK: @s128 = external dso_local global i128
+// CHECK: @s128 = dso_local global i128 0
 
 unsigned __int128 u128;
-// CHECK: @u128 = external dso_local global i128
+// CHECK: @u128 = dso_local global i128 0
 
 wchar_t wc;
-// CHECK: @wc = external dso_local global i32
+// CHECK: @wc = dso_local global i32 0
 
 char8_t c8;
-// CHECK: @c8 = external dso_local global i8
+// CHECK: @c8 = dso_local global i8 0
 
 char16_t c16;
-// CHECK: @c16 = external dso_local global i16
+// CHECK: @c16 = dso_local global i16 0
 
 char32_t c32;
-// CHECK: @c32 = external dso_local global i32
+// CHECK: @c32 = dso_local global i32 0
 
 _BitInt(20) sb20;
-// CHECK: @sb20 = external dso_local global i20
+// CHECK: @sb20 = dso_local global i20 0
 
 unsigned _BitInt(48) ub48;
-// CHECK: @ub48 = external dso_local global i48
+// CHECK: @ub48 = dso_local global i48 0
 
 bool boolfalse = false;
 // CHECK: @boolfalse = dso_local global i8 0
 
 _Float16 f16;
-// CHECK: @f16 = external dso_local global half
+// CHECK: @f16 = dso_local global half
 
 __bf16 bf16;
-// CHECK: @bf16 = external dso_local global bfloat
+// CHECK: @bf16 = dso_local global bfloat
 
 float f;
-// CHECK: @f = external dso_local global float
+// CHECK: @f = dso_local global float 0.000000e+00
 
 double d = 1.25;
 // CHECK: @d = dso_local global double 1.250000e+00
 
 long double ld;
-// CHECK: @ld = external dso_local global x86_fp80
+// CHECK: @ld = dso_local global x86_fp80 0xK00
 
 __float128 f128;
-// CHECK: @f128 = external dso_local global fp128
+// CHECK: @f128 = dso_local global fp128 0xL00
 
 void *vp;
-// CHECK: @vp = external dso_local global ptr{{$}}
+// CHECK: @vp = dso_local global ptr null
 
 int *ip = 0;
 // CHECK: @ip = dso_local global ptr null
 
 double *dp;
-// CHECK: @dp = external dso_local global ptr{{$}}
+// CHECK: @dp = dso_local global ptr null
 
 char **cpp;
-// CHECK: @cpp = external dso_local global ptr{{$}}
+// CHECK: @cpp = dso_local global ptr null
 
 void (*fp)();
-// CHECK: @fp = external dso_local global ptr{{$}}
+// CHECK: @fp = dso_local global ptr null
 
 int (*fpii)(int) = 0;
 // CHECK: @fpii = dso_local global ptr null
 
 void (*fpvar)(int, ...);
-// CHECK: @fpvar = external dso_local global ptr{{$}}
+// CHECK: @fpvar = dso_local global ptr null
diff --git a/clang/test/CIR/Lowering/hello.c b/clang/test/CIR/Lowering/hello.c
index ff78b6e6f6a5e..f45beafdcb533 100644
--- a/clang/test/CIR/Lowering/hello.c
+++ b/clang/test/CIR/Lowering/hello.c
@@ -3,7 +3,7 @@
 
 int a;
 
-// CHECK: @a = external dso_local global i32
+// CHECK: @a = dso_local global i32 0
 
 int b = 2;
 
diff --git a/clang/test/CIR/global-var-simple.cpp b/clang/test/CIR/global-var-simple.cpp
index 020ef5f09c650..9a52925303504 100644
--- a/clang/test/CIR/global-var-simple.cpp
+++ b/clang/test/CIR/global-var-simple.cpp
@@ -2,16 +2,16 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o -  | FileCheck %s
 
 char c;
-// CHECK: cir.global external @c : !s8i
+// CHECK: cir.global external @c = #cir.int<0> : !s8i
 
 signed char sc;
-// CHECK: cir.global external @sc : !s8i
+// CHECK: cir.global external @sc = #cir.int<0> : !s8i
 
 unsigned char uc;
-// CHECK: cir.global external @uc : !u8i
+// CHECK: cir.global external @uc = #cir.int<0> : !u8i
 
 short ss;
-// CHECK: cir.global external @ss : !s16i
+// CHECK: cir.global external @ss = #cir.int<0> : !s16i
 
 unsigned short us = 100;
 // CHECK: cir.global external @us = #cir.int<100> : !u16i
@@ -20,82 +20,82 @@ int si = 42;
 // CHECK: cir.global external @si = #cir.int<42> : !s32i
 
 unsigned ui;
-// CHECK: cir.global external @ui : !u32i
+// CHECK: cir.global external @ui = #cir.int<0> : !u32i
 
 long sl;
-// CHECK: cir.global external @sl : !s64i
+// CHECK: cir.global external @sl = #cir.int<0> : !s64i
 
 unsigned long ul;
-// CHECK: cir.global external @ul : !u64i
+// CHECK: cir.global external @ul = #cir.int<0> : !u64i
 
 long long sll;
-// CHECK: cir.global external @sll : !s64i
+// CHECK: cir.global external @sll = #cir.int<0> : !s64i
 
 unsigned long long ull = 123456;
 // CHECK: cir.global external @ull = #cir.int<123456> : !u64i
 
 __int128 s128;
-// CHECK: cir.global external @s128 : !s128i
+// CHECK: cir.global external @s128 = #cir.int<0> : !s128i
 
 unsigned __int128 u128;
-// CHECK: cir.global external @u128 : !u128i
+// CHECK: cir.global external @u128 = #cir.int<0> : !u128i
 
 wchar_t wc;
-// CHECK: cir.global external @wc : !s32i
+// CHECK: cir.global external @wc = #cir.int<0> : !s32i
 
 char8_t c8;
-// CHECK: cir.global external @c8 : !u8i
+// CHECK: cir.global external @c8 = #cir.int<0> : !u8i
 
 char16_t c16;
-// CHECK: cir.global external @c16 : !u16i
+// CHECK: cir.global external @c16 = #cir.int<0> : !u16i
 
 char32_t c32;
-// CHECK: cir.global external @c32 : !u32i
+// CHECK: cir.global external @c32 = #cir.int<0> : !u32i
 
 _BitInt(20) sb20;
-// CHECK: cir.global external @sb20 : !cir.int<s, 20>
+// CHECK: cir.global external @sb20 = #cir.int<0> : !cir.int<s, 20>
 
 unsigned _BitInt(48) ub48;
-// CHECK: cir.global external @ub48 : !cir.int<u, 48>
+// CHECK: cir.global external @ub48 = #cir.int<0> : !cir.int<u, 48>
 
 bool boolfalse = false;
 // CHECK: cir.global external @boolfalse = #false
 
 _Float16 f16;
-// CHECK: cir.global external @f16 : !cir.f16
+// CHECK: cir.global external @f16 = #cir.fp<0.000000e+00> : !cir.f16
 
 __bf16 bf16;
-// CHECK: cir.global external @bf16 : !cir.bf16
+// CHECK: cir.global external @bf16 = #cir.fp<0.000000e+00> : !cir.bf16
 
 float f;
-// CHECK: cir.global external @f : !cir.float
+// CHECK: cir.global external @f = #cir.fp<0.000000e+00>  : !cir.float
 
 double d = 1.25;
 // CHECK: cir.global external @d = #cir.fp<1.250000e+00> : !cir.double
 
 long double ld;
-// CHECK: cir.global external @ld : !cir.long_double<!cir.f80>
+// CHECK: cir.global external @ld = #cir.fp<0.000000e+00> : !cir.long_double<!cir.f80>
 
 __float128 f128;
-// CHECK: cir.global external @f128 : !cir.f128
+// CHECK: cir.global external @f128 = #cir.fp<0.000000e+00> : !cir.f128
 
 void *vp;
-// CHECK: cir.global external @vp : !cir.ptr<!void>
+// CHECK: cir.global external @vp = #cir.ptr<null> : !cir.ptr<!void>
 
 int *ip = 0;
 // CHECK: cir.global external @ip = #cir.ptr<null> : !cir.ptr<!s32i>
 
 double *dp;
-// CHECK: cir.global external @dp : !cir.ptr<!cir.double>
+// CHECK: cir.global external @dp = #cir.ptr<null> : !cir.ptr<!cir.double>
 
 char **cpp;
-// CHECK: cir.global external @cpp : !cir.ptr<!cir.ptr<!s8i>>
+// CHECK: cir.global external @cpp = #cir.ptr<null> : !cir.ptr<!cir.ptr<!s8i>>
 
 void (*fp)();
-// CHECK: cir.global external @fp : !cir.ptr<!cir.func<()>>
+// CHECK: cir.global external @fp = #cir.ptr<null> : !cir.ptr<!cir.func<()>>
 
 int (*fpii)(int) = 0;
 // CHECK: cir.global external @fpii = #cir.ptr<null> : !cir.ptr<!cir.func<(!s32i) -> !s32i>>
 
 void (*fpvar)(int, ...);
-// CHECK: cir.global external @fpvar : !cir.ptr<!cir.func<(!s32i, ...)>>
+// CHECK: cir.global external @fpvar = #cir.ptr<null> : !cir.ptr<!cir.func<(!s32i, ...)>>

@AmrDeveloper AmrDeveloper force-pushed the upstream_globals_zero_init branch from 999b8e4 to b4aafe8 Compare March 26, 2025 14:58
return cir::ZeroAttr::get(getContext(), t);
}

mlir::TypedAttr getZeroInitAttr(mlir::Type ty) {
Copy link
Member Author

Choose a reason for hiding this comment

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

We need to update this function in the incubator to add the missing types @andykaylor

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Overall looks fine!

mlir::TypedAttr getZeroInitAttr(mlir::Type ty) {
if (mlir::isa<cir::IntType>(ty))
return cir::IntAttr::get(ty, 0);
if (auto fltType = mlir::dyn_cast<cir::SingleType>(ty))
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this will work:

if (cir::isAnyFloatingPointType(ty))
  return cir::FPAttr::getZero(ty);

Copy link
Member

@bcardosolopes bcardosolopes left a comment

Choose a reason for hiding this comment

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

Thanks for the updates, LGTM!

Copy link
Contributor

@andykaylor andykaylor left a comment

Choose a reason for hiding this comment

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

lgtm

@AmrDeveloper AmrDeveloper merged commit 143c371 into llvm:main Mar 31, 2025
11 checks passed
bcardosolopes pushed a commit to llvm/clangir that referenced this pull request Apr 4, 2025
Update getZeroInitAttr to cover more FP types, Backport from
(llvm/llvm-project#133100)
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.

4 participants