[clang] [CIR] Correctly pass func self-comdat & alignment (PR #223773)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 18 06:19:00 PDT 2026


https://github.com/erichkeane updated https://github.com/llvm/llvm-project/pull/223773

>From c74f15825a381760a1c26d46c4bce6d752041586 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Tue, 15 Sep 2026 08:39:33 -0700
Subject: [PATCH 1/3] [CIR] Correctly pass func self-comdat & alignment

Classic codegen does these, and it is important for when these end up
being stored as function pointers, as we discovered on a benchmark.

This patch does 2 things:

1- Properly passes the 'self' 'comdat' feature (that is, when the symbol
    reference is 'self', not when it is a different name, which isn't
    implemented anywhere).  This mirrors what was done for GlobalOp.

2- Properly calculate and pass the 'alignment'/'preferred alignment' for a
function. Only the 'alignment' is passed to LLVM-IR, as the dialect
doesn't support preferred alignment, so a 'missing feature' is left in
place. That functionality isn't important to this patch, but was 'next
to' the alignment work in CodeGenModule.cpp, so it seemed like something
we should handle if at all possible.
---
 clang/include/clang/CIR/Dialect/IR/CIROps.td  |  6 +++
 clang/include/clang/CIR/MissingFeatures.h     |  1 +
 clang/lib/CIR/CodeGen/CIRGenModule.cpp        | 23 +++++++++
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       | 17 +++++++
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 51 ++++++++++++++-----
 .../CIR/CodeGen/array-init-loop-exprs.cpp     |  2 +-
 .../CIR/CodeGen/cxx-conversion-operators.cpp  |  2 +-
 clang/test/CIR/CodeGen/func-member-attrs.cpp  | 43 ++++++++++++++++
 .../test/CIR/CodeGen/global-tls-dyn-init.cpp  |  8 +--
 .../CIR/CodeGen/global-tls-simple-init.cpp    | 14 ++---
 .../test/CIR/CodeGen/global-tls-templates.cpp |  4 +-
 clang/test/CIR/CodeGen/inherited-ctors.cpp    |  6 +--
 .../lambda-generic-in-cxx11abi-lambda.cpp     |  4 +-
 .../lambda-static-invoker-agg-return.cpp      |  2 +-
 .../CIR/CodeGen/lambda-static-invoker.cpp     | 12 ++---
 clang/test/CIR/CodeGen/lambda.cpp             | 10 ++--
 clang/test/CIR/CodeGen/static-local.cpp       |  6 +--
 clang/test/CIR/CodeGen/vtable-emission.cpp    |  2 +-
 .../CodeGen/vtable-thunk-undefined-callee.cpp |  2 +-
 clang/test/CIR/IR/func.cir                    | 14 +++++
 clang/test/CIR/IR/invalid-func.cir            | 27 ++++++++++
 clang/test/CIR/func-linkage.cpp               | 10 +---
 22 files changed, 207 insertions(+), 59 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/func-member-attrs.cpp

diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index c5a836a0c2f03..7e9dcfc2ecbdd 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4293,6 +4293,8 @@ def CIR_FuncOp : CIR_Op<"func", [
     >:$calling_conv,
     OptionalAttr<StrAttr>:$sym_visibility,
     UnitAttr:$comdat,
+    OptionalAttr<I64Attr>:$alignment,
+    OptionalAttr<I64Attr>:$preferred_alignment,
     OptionalAttr<DictArrayAttr>:$arg_attrs,
     OptionalAttr<DictArrayAttr>:$res_attrs,
     OptionalAttr<FlatSymbolRefAttr>:$aliasee,
@@ -4385,6 +4387,10 @@ def CIR_FuncOp : CIR_Op<"func", [
     matchAndRewriteAlias(cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty,
                          OpAdaptor adaptor,
                          mlir::ConversionPatternRewriter &rewriter) const;
+
+    mutable mlir::LLVM::ComdatOp comdatOp = nullptr;
+    mlir::SymbolRefAttr getComdatAttr(cir::FuncOp &op,
+                                      mlir::OpBuilder &builder) const;
   }];
 }
 
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 16d97871ec7b6..a82d0e0a4729d 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -82,6 +82,7 @@ struct MissingFeatures {
   static bool opFuncReadOnly() { return false; }
   static bool opFuncUnwindTablesAttr() { return false; }
   static bool opFuncWillReturn() { return false; }
+  static bool opFuncPreferredAlignment() { return false; }
   static bool setLLVMFunctionFEnvAttributes() { return false; }
 
   // CallOp handling
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index bdc2707723283..e8780fc0ba84e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -3379,6 +3379,29 @@ void CIRGenModule::setCIRFunctionAttributesForDefinition(
   }
 
   assert(!cir::MissingFeatures::opFuncColdHotAttr());
+
+  std::optional<uint64_t> explicitAlignment;
+  if (unsigned alignment =
+          decl->getMaxAlignment() / getASTContext().getCharWidth())
+    explicitAlignment = alignment;
+  else if (langOpts.FunctionAlignment)
+    explicitAlignment = 1ull << langOpts.FunctionAlignment;
+
+  if (explicitAlignment) {
+    f.setAlignment(*explicitAlignment);
+    f.setPreferredAlignment(*explicitAlignment);
+  } else if (langOpts.PreferredFunctionAlignment) {
+    f.setPreferredAlignment(langOpts.PreferredFunctionAlignment);
+  }
+
+  // Some C++ ABIs require 2-byte alignment for member functions, in order to
+  // reserve a bit for differentiating between virtual and non-virtual member
+  // functions. If the current target's C++ ABI requires this and this is a
+  // member function, set its alignment accordingly.
+  if (getTarget().getCXXABI().areMemberFunctionsAligned()) {
+    if (isa<CXXMethodDecl>(decl) && f.getAlignment().value_or(1) < 2)
+      f.setAlignment(std::max(2ul, f.getAlignment().value_or(1)));
+  }
 }
 
 // Maps an AST address space to the OpenCL logical address space kind recorded
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 4782de43bec5f..ed99bae5e3c78 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2597,6 +2597,7 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
   mlir::StringAttr lambdaNameAttr = getLambdaAttrName(state.name);
   mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name);
   mlir::StringAttr comdatNameAttr = getComdatAttrName(state.name);
+  mlir::StringAttr alignmentNameAttr = getAlignmentAttrName(state.name);
   mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
   mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
   mlir::StringAttr funcInfoNameAttr = getFuncInfoAttrName(state.name);
@@ -2622,6 +2623,19 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
   if (parser.parseOptionalKeyword(comdatNameAttr).succeeded())
     state.addAttribute(comdatNameAttr, parser.getBuilder().getUnitAttr());
 
+  if (parser.parseOptionalKeyword(alignmentNameAttr).succeeded()) {
+    int64_t value;
+    if (parser.parseLParen().failed() || parser.parseInteger(value).failed() ||
+        parser.parseRParen().failed())
+      return failure();
+
+    if (value <= 0)
+      return parser.emitError(loc,
+                              "function alignment must be a positive integer");
+
+    state.addAttribute(alignmentNameAttr, builder.getI64IntegerAttr(value));
+  }
+
   // Default to external linkage if no keyword is provided.
   state.addAttribute(getLinkageAttrNameString(),
                      GlobalLinkageKindAttr::get(
@@ -2933,6 +2947,9 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
   if (getComdat())
     p << " comdat";
 
+  if (getAlignment())
+    p << " alignment(" << *getAlignment() << ')';
+
   if (getLinkage() != GlobalLinkageKind::ExternalLinkage)
     p << ' ' << stringifyGlobalLinkageKind(getLinkage());
 
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 24ee700e87ece..13dab4c9e26d3 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2694,7 +2694,9 @@ static bool shouldDropFuncAttribute(cir::FuncOp func, mlir::NamedAttribute attr,
          attr.getName() == func.getSideEffectAttrName() ||
          attr.getName() == CIRDialect::getNoReturnAttrName() ||
          attr.getName() == CIRDialect::getStrictFPAttrName() ||
-         attr.getName() == func.getAnnotationsAttrName();
+         attr.getName() == func.getAnnotationsAttrName() ||
+         attr.getName() == func.getComdatAttrName() ||
+         attr.getName() == func.getAlignmentAttrName();
 }
 
 /// Lower `cir.func` attributes for an `LLVMFuncOp` or `LLVM::AliasOp`.
@@ -2804,9 +2806,11 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
                                        attributes)))
     return mlir::failure();
 
+  mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
+
   mlir::LLVM::LLVMFuncOp fn = mlir::LLVM::LLVMFuncOp::create(
       rewriter, loc, op.getName(), llvmFnTy, linkage, isDsoLocal, cconv,
-      mlir::SymbolRefAttr(), attributes);
+      comdatAttr, attributes);
 
   assert(!cir::MissingFeatures::opFuncMultipleReturnVals());
 
@@ -2864,6 +2868,9 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
   fn.setVisibility_(
       lowerCIRVisibilityToLLVMVisibility(op.getGlobalVisibility()));
 
+  fn.setAlignment(op.getAlignment());
+  assert(!MissingFeatures::opFuncPreferredAlignment());
+
   rewriter.inlineRegionBefore(op.getBody(), fn.getBody(), fn.end());
   if (failed(rewriter.convertRegionTypes(&fn.getBody(), *typeConverter,
                                          &signatureConversion)))
@@ -3142,23 +3149,20 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
   return mlir::success();
 }
 
-mlir::SymbolRefAttr
-CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
-                                         mlir::OpBuilder &builder) const {
-  if (!op.getComdat())
-    return mlir::SymbolRefAttr{};
-
-  mlir::ModuleOp modOp = op->getParentOfType<mlir::ModuleOp>();
+static mlir::SymbolRefAttr getComdatAttrHelper(mlir::ModuleOp modOp,
+                                               mlir::OpBuilder &builder,
+                                               StringRef symName,
+                                               mlir::LLVM::ComdatOp &comdatOp,
+                                               StringRef comdatName) {
   mlir::OpBuilder::InsertionGuard guard(builder);
-  StringRef comdatName("__llvm_comdat_globals");
   if (!comdatOp) {
     builder.setInsertionPointToStart(modOp.getBody());
     comdatOp =
         mlir::LLVM::ComdatOp::create(builder, modOp.getLoc(), comdatName);
   }
 
-  if (auto comdatSelector = comdatOp.lookupSymbol<mlir::LLVM::ComdatSelectorOp>(
-          op.getSymName())) {
+  if (auto comdatSelector =
+          comdatOp.lookupSymbol<mlir::LLVM::ComdatSelectorOp>(symName)) {
     return mlir::SymbolRefAttr::get(
         builder.getContext(), comdatName,
         mlir::FlatSymbolRefAttr::get(comdatSelector.getSymNameAttr()));
@@ -3166,13 +3170,32 @@ CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
 
   builder.setInsertionPointToStart(&comdatOp.getBody().back());
   auto selectorOp = mlir::LLVM::ComdatSelectorOp::create(
-      builder, comdatOp.getLoc(), op.getSymName(),
-      mlir::LLVM::comdat::Comdat::Any, /*sym_visibility=*/nullptr);
+      builder, comdatOp.getLoc(), symName, mlir::LLVM::comdat::Comdat::Any,
+      /*sym_visibility=*/nullptr);
   return mlir::SymbolRefAttr::get(
       builder.getContext(), comdatName,
       mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr()));
 }
 
+mlir::SymbolRefAttr
+CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
+                                         mlir::OpBuilder &builder) const {
+  if (!op.getComdat())
+    return mlir::SymbolRefAttr{};
+  return getComdatAttrHelper(op->getParentOfType<mlir::ModuleOp>(), builder,
+                             op.getSymName(), comdatOp,
+                             "__llvm_comdat_globals");
+}
+
+mlir::SymbolRefAttr
+CIRToLLVMFuncOpLowering::getComdatAttr(cir::FuncOp &op,
+                                       mlir::OpBuilder &builder) const {
+  if (!op.getComdat())
+    return mlir::SymbolRefAttr{};
+  return getComdatAttrHelper(op->getParentOfType<mlir::ModuleOp>(), builder,
+                             op.getSymName(), comdatOp, "__llvm_comdat_funcs");
+}
+
 mlir::LogicalResult CIRToLLVMSwitchFlatOpLowering::matchAndRewrite(
     cir::SwitchFlatOp op, OpAdaptor adaptor,
     mlir::ConversionPatternRewriter &rewriter) const {
diff --git a/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp b/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp
index 172a3d34573e1..f5907892fe53e 100644
--- a/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp
+++ b/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp
@@ -49,7 +49,7 @@ struct HasMultiDimArray {
 // CIR-LABEL: cir.func {{.*}}@_ZN16HasMultiDimArrayC1ERKS_(
 // CIR:    cir.call @_ZN16HasMultiDimArrayC2ERKS_(
 
-// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN18HasNonTrivialArrayC2ERKS_({{.*}}) func_info<#cir.cxx_ctor<!rec_HasNonTrivialArray, copy>> 
+// CIR-LABEL: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN18HasNonTrivialArrayC2ERKS_({{.*}}) func_info<#cir.cxx_ctor<!rec_HasNonTrivialArray, copy>> 
 // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>>
 // CIR: %[[RHS_ALLOCA:.*]] = cir.alloca "" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>>
 // CIR: %[[ITR_ALLOCA:.*]] = cir.alloca "arrayinit.temp" {{.*}} : !cir.ptr<!cir.ptr<!rec_NonTrivial>>
diff --git a/clang/test/CIR/CodeGen/cxx-conversion-operators.cpp b/clang/test/CIR/CodeGen/cxx-conversion-operators.cpp
index 989145c9f8e96..21dfbf2334645 100644
--- a/clang/test/CIR/CodeGen/cxx-conversion-operators.cpp
+++ b/clang/test/CIR/CodeGen/cxx-conversion-operators.cpp
@@ -51,7 +51,7 @@ void test() {
 // CIR:   cir.return
 // CIR: }
 
-// CIR: cir.func no_inline comdat linkonce_odr @_ZNK15inline_operatorcviEv(%[[INLINE_THIS_ARG:.+]]: !cir.ptr<!rec_inline_operator>{{.*}}) -> (!s32i{{.*}})
+// CIR: cir.func no_inline comdat alignment(2) linkonce_odr @_ZNK15inline_operatorcviEv(%[[INLINE_THIS_ARG:.+]]: !cir.ptr<!rec_inline_operator>{{.*}}) -> (!s32i{{.*}})
 // CIR:   %[[INLINE_THIS_ALLOCA:.+]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_inline_operator>>
 // CIR:   %[[INLINE_RETVAL:.+]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i>
 // CIR:   cir.store %[[INLINE_THIS_ARG]], %[[INLINE_THIS_ALLOCA]] : !cir.ptr<!rec_inline_operator>, !cir.ptr<!cir.ptr<!rec_inline_operator>>
diff --git a/clang/test/CIR/CodeGen/func-member-attrs.cpp b/clang/test/CIR/CodeGen/func-member-attrs.cpp
new file mode 100644
index 0000000000000..b62c46d7591a1
--- /dev/null
+++ b/clang/test/CIR/CodeGen/func-member-attrs.cpp
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck %s -check-prefix=CIR --input-file %t.cir
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck %s -check-prefix=LLVM --input-file %t-cir.ll
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck %s -check-prefix=LLVM --input-file %t.ll
+
+struct S {
+  void regular_method();
+
+  void inline_method() {}
+
+  void __attribute__((aligned(8))) aligned_method() {}
+
+  static void static_method() {}
+};
+
+void S::regular_method() {}
+
+inline void free_inline() {}
+
+void use(S &s) {
+  s.regular_method();
+  s.inline_method();
+  s.aligned_method();
+  S::static_method();
+  free_inline();
+}
+
+// CIR: cir.func no_inline alignment(2) dso_local @_ZN1S14regular_methodEv(
+// LLVM: define dso_local void @_ZN1S14regular_methodEv({{.*}}) #{{[0-9]+}} align 2
+
+// CIR: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN1S13inline_methodEv(
+// LLVM: define linkonce_odr void @_ZN1S13inline_methodEv({{.*}}) #{{[0-9]+}} comdat align 2
+
+// CIR: cir.func no_inline comdat alignment(8) linkonce_odr @_ZN1S14aligned_methodEv(
+// LLVM: define linkonce_odr void @_ZN1S14aligned_methodEv({{.*}}) #{{[0-9]+}} comdat align 8
+
+// CIR: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN1S13static_methodEv(
+// LLVM: define linkonce_odr void @_ZN1S13static_methodEv() #{{[0-9]+}} comdat align 2
+
+// CIR: cir.func no_inline comdat linkonce_odr @_Z11free_inlinev()
+// LLVM: define linkonce_odr void @_Z11free_inlinev() #{{[0-9]+}} comdat {
diff --git a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
index 9b4422374fadb..fe6288f331227 100644
--- a/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-dyn-init.cpp
@@ -63,25 +63,25 @@ struct CtorDtor {
 // CIR:  }
 // CIR:  cir.return
 
-// LLVM: define weak_odr hidden ptr @_ZTW19tls_cd_dyn_not_used() {
+// LLVM: define weak_odr hidden ptr @_ZTW19tls_cd_dyn_not_used() comdat {
 // LLVM:   call void @_ZTH19tls_cd_dyn_not_used()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn_not_used)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
-// LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_ref() {
+// LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_ref() comdat {
 // LLVM:   call void @_ZTH10tls_cd_ref()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_ref)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
-// LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_dyn() {
+// LLVM: define weak_odr hidden ptr @_ZTW10tls_cd_dyn() comdat {
 // LLVM:   call void @_ZTH10tls_cd_dyn()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd_dyn)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
-// LLVM: define weak_odr hidden ptr @_ZTW6tls_cd() {
+// LLVM: define weak_odr hidden ptr @_ZTW6tls_cd() comdat {
 // LLVM:   call void @_ZTH6tls_cd()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_cd)
 // LLVM:   ret ptr %[[GET_GLOB]]
diff --git a/clang/test/CIR/CodeGen/global-tls-simple-init.cpp b/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
index a324cd63bf7c2..049e8fd9f16d7 100644
--- a/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-simple-init.cpp
@@ -87,34 +87,34 @@ struct CtorDtor {
 // LLVM-BOTH-DAG: @_ZTH11tls_int_dyn = alias void (), ptr @__tls_init
 
 // Wrappers: 
-// LLVM: define linkonce_odr hidden ptr @_ZTW12maybe_inited() {
+// LLVM: define linkonce_odr hidden ptr @_ZTW12maybe_inited() comdat {
 // LLVM:   %[[HAS_INIT_FUNC:.*]] = icmp ne ptr @_ZTH12maybe_inited, null
 // LLVM:   br i1 %[[HAS_INIT_FUNC]]
 // LLVM:   call void @_ZTH12maybe_inited()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @maybe_inited)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
-// LLVM: define weak_odr hidden ptr @_ZTW21definitely_inited_dyn() {
+// LLVM: define weak_odr hidden ptr @_ZTW21definitely_inited_dyn() comdat {
 // LLVM:   call void @_ZTH21definitely_inited_dyn()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @definitely_inited_dyn)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
-// LLVM: define weak_odr hidden ptr @_ZTW17definitely_inited() {
+// LLVM: define weak_odr hidden ptr @_ZTW17definitely_inited() comdat {
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @definitely_inited)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
 //
-// LLVM: define weak_odr hidden ptr @_ZTW17tls_int_self_init() {
+// LLVM: define weak_odr hidden ptr @_ZTW17tls_int_self_init() comdat {
 // LLVM:   call void @_ZTH17tls_int_self_init()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int_self_init)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
-// LLVM: define weak_odr hidden ptr @_ZTW11tls_int_ref() {
+// LLVM: define weak_odr hidden ptr @_ZTW11tls_int_ref() comdat {
 // LLVM:   call void @_ZTH11tls_int_ref()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int_ref)
 // LLVM:   ret ptr %[[GET_GLOB]]
 //
-// LLVM: define weak_odr hidden ptr @_ZTW11tls_int_dyn() {
+// LLVM: define weak_odr hidden ptr @_ZTW11tls_int_dyn() comdat {
 // LLVM:   call void @_ZTH11tls_int_dyn()
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int_dyn)
 // LLVM:   ret ptr %[[GET_GLOB]]
@@ -133,7 +133,7 @@ struct CtorDtor {
 // LLVM:   br label 
 // LLVM:   ret void
 
-// LLVM: define weak_odr hidden ptr @_ZTW7tls_int() {
+// LLVM: define weak_odr hidden ptr @_ZTW7tls_int() comdat {
 // LLVM:   %[[GET_GLOB:.*]] = call ptr @llvm.threadlocal.address.p0(ptr @tls_int)
 // LLVM:   ret ptr %[[GET_GLOB]]
 // LLVM: }
diff --git a/clang/test/CIR/CodeGen/global-tls-templates.cpp b/clang/test/CIR/CodeGen/global-tls-templates.cpp
index f0384aa3373b9..f2e6cac958422 100644
--- a/clang/test/CIR/CodeGen/global-tls-templates.cpp
+++ b/clang/test/CIR/CodeGen/global-tls-templates.cpp
@@ -120,12 +120,12 @@ thread_local T tls_templ = {get_i()};
 // Wrappers: Just opposite ordering, same check lines as LLVM.
 // FIXME: OGCG has these set as 'comdat'. However, CIR doesn't lower comdat to
 // LLVM, so it doesn't show up in the IR here.
-// LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templI8CtorDtorE() {
+// LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templI8CtorDtorE() comdat {
 // LLVM:   call void @_ZTH9tls_templI8CtorDtorE()
 // LLVM:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templI8CtorDtorE)
 // LLVM: }
 
-// LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() {
+// LLVM-LABEL: define weak_odr hidden {{.*}}ptr @_ZTW9tls_templIiE() comdat {
 // LLVM:   call void @_ZTH9tls_templIiE()
 // LLVM:   call {{.*}}ptr @llvm.threadlocal.address.p0(ptr {{.*}}@_Z9tls_templIiE)
 // LLVM: }
diff --git a/clang/test/CIR/CodeGen/inherited-ctors.cpp b/clang/test/CIR/CodeGen/inherited-ctors.cpp
index efbd5533c8d5f..e78279018772b 100644
--- a/clang/test/CIR/CodeGen/inherited-ctors.cpp
+++ b/clang/test/CIR/CodeGen/inherited-ctors.cpp
@@ -66,7 +66,7 @@ void fallsthrough() {
 // LLVM: call void @_ZN21VirtualDelegatingCtorC1Ei(ptr {{.*}}, i32 {{.*}}1)
 //
 //
-// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN21VirtualDelegatingCtorC1Ei(%{{.*}}: !cir.ptr<!rec_VirtualDelegatingCtor> {{.*}}, %{{.*}}: !s32i {{.*}}) func_info<#cir.cxx_ctor<!rec_VirtualDelegatingCtor, custom>>
+// CIR-LABEL: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN21VirtualDelegatingCtorC1Ei(%{{.*}}: !cir.ptr<!rec_VirtualDelegatingCtor> {{.*}}, %{{.*}}: !s32i {{.*}}) func_info<#cir.cxx_ctor<!rec_VirtualDelegatingCtor, custom>>
 // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" align(8) init : !cir.ptr<!cir.ptr<!rec_VirtualDelegatingCtor>>
 // CIR: %[[X_ALLOCA:.*]] = cir.alloca "x" align(4) init : !cir.ptr<!s32i>
 // CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtualDelegatingCtor>>, !cir.ptr<!rec_VirtualDelegatingCtor>
@@ -97,7 +97,7 @@ void fallsthrough() {
 // LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV21VirtualDelegatingCtor, i64 24), ptr %[[THIS_LOAD]]
 //
 
-// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN7DerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_Derived>{{.*}}, %{{.*}}: !s32i{{.*}}) func_info<#cir.cxx_ctor<!rec_Derived, custom>>
+// CIR-LABEL: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN7DerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_Derived>{{.*}}, %{{.*}}: !s32i{{.*}}) func_info<#cir.cxx_ctor<!rec_Derived, custom>>
 // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_Derived>>
 // CIR: %[[INT_ALLOCA:.*]] = cir.alloca "" {{.*}} init : !cir.ptr<!s32i>
 // CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_Derived>>, !cir.ptr<!rec_Derived>
@@ -117,7 +117,7 @@ void fallsthrough() {
 // LLVM-LABEL: declare void @_ZN4BaseC2Ei(ptr {{.*}}, i32 {{.*}})
 //
 //
-// CIR-LABEL: cir.func no_inline comdat linkonce_odr @_ZN11VirtDerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_VirtDerived> {{.*}}, %{{.*}}: !cir.ptr<!cir.ptr<!void>>{{.*}}) func_info<#cir.cxx_ctor<!rec_VirtDerived, custom>>
+// CIR-LABEL: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN11VirtDerivedCI24BaseEi(%{{.*}}: !cir.ptr<!rec_VirtDerived> {{.*}}, %{{.*}}: !cir.ptr<!cir.ptr<!void>>{{.*}}) func_info<#cir.cxx_ctor<!rec_VirtDerived, custom>>
 // CIR: %[[THIS_ALLOCA:.*]] = cir.alloca "this" align(8) init : !cir.ptr<!cir.ptr<!rec_VirtDerived>>
 // CIR: %[[VTT_ALLOCA:.]] = cir.alloca "vtt" align(8) init : !cir.ptr<!cir.ptr<!cir.ptr<!void>>>
 // CIR: %[[THIS:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!rec_VirtDerived>>, !cir.ptr<!rec_VirtDerived>
diff --git a/clang/test/CIR/CodeGen/lambda-generic-in-cxx11abi-lambda.cpp b/clang/test/CIR/CodeGen/lambda-generic-in-cxx11abi-lambda.cpp
index 0ad64bc4a7661..ee0747fa05806 100644
--- a/clang/test/CIR/CodeGen/lambda-generic-in-cxx11abi-lambda.cpp
+++ b/clang/test/CIR/CodeGen/lambda-generic-in-cxx11abi-lambda.cpp
@@ -15,7 +15,7 @@ void g() {
 }
 int main() { g(); }
 
-// CIR-LABEL: cir.func no_inline lambda internal private dso_local @_ZZ1gvENK3$_0clB5cxx11Ev
+// CIR-LABEL: cir.func no_inline lambda alignment(2) internal private dso_local @_ZZ1gvENK3$_0clB5cxx11Ev
 // CIR-NOT: define
 // CIR: %[[ONE:.*]] = cir.const #cir.int<1> 
 // CIR: cir.call @_Z1fIZZ1gvENK3$_0clEvEUlT_E_EDaiS1_(%[[ONE]], %{{.*}})
@@ -33,7 +33,7 @@ int main() { g(); }
 // LLVM-NOT: define
 // LLVM: call noundef i32 @"_ZZZ1gvENK3$_0clEvENKUlT_E_clIiEEDaS0_"
 
-// CIR-LABEL: cir.func no_inline lambda internal private dso_local @_ZZZ1gvENK3$_0clEvENKUlT_E_clIiEEDaS0_
+// CIR-LABEL: cir.func no_inline lambda alignment(2) internal private dso_local @_ZZZ1gvENK3$_0clEvENKUlT_E_clIiEEDaS0_
 // LLVM-LABEL: define internal noundef i32 @"_ZZZ1gvENK3$_0clEvENKUlT_E_clIiEEDaS0_"
 
 // OGCG-LABEL: define internal i32 @"_ZZ1gvENK3$_0clB5cxx11Ev"(
diff --git a/clang/test/CIR/CodeGen/lambda-static-invoker-agg-return.cpp b/clang/test/CIR/CodeGen/lambda-static-invoker-agg-return.cpp
index 0cc0b577d7114..641055a94461a 100644
--- a/clang/test/CIR/CodeGen/lambda-static-invoker-agg-return.cpp
+++ b/clang/test/CIR/CodeGen/lambda-static-invoker-agg-return.cpp
@@ -19,7 +19,7 @@ S agg_invoker() {
   return fn(3);
 }
 
-// CIR-LABEL: cir.func no_inline internal private dso_local @_ZZ11agg_invokervEN3$_08__invokeEi
+// CIR-LABEL: cir.func no_inline alignment(2) internal private dso_local @_ZZ11agg_invokervEN3$_08__invokeEi
 // CIR-SAME:    (%[[AGG_RESULT:.*]]: !cir.ptr<!rec_S> {{.*}}llvm.sret = !rec_S{{.*}}, %[[I_ARG:.*]]: !s32i {{.*}})
 // CIR:         %[[I_ALLOCA:.*]] = cir.alloca "i" {{.*}} init : !cir.ptr<!s32i>
 // CIR:         %[[UNUSED:.*]] = cir.alloca "unused.capture" {{.*}} : !cir.ptr<!rec_anon{{.*}}>
diff --git a/clang/test/CIR/CodeGen/lambda-static-invoker.cpp b/clang/test/CIR/CodeGen/lambda-static-invoker.cpp
index c2be35a0c7c55..deb09ba9710b3 100644
--- a/clang/test/CIR/CodeGen/lambda-static-invoker.cpp
+++ b/clang/test/CIR/CodeGen/lambda-static-invoker.cpp
@@ -38,7 +38,7 @@ int g3() {
 // CIR:   cir.return %[[RET]]
 
 // lambda operator int (*)(int const&)()
-// CIR:   cir.func no_inline internal private dso_local @_ZZ2g3vENK3$_0cvPFiRKiEEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G3]]> {{.*}}) -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>{{.*}}){{.*}} {
+// CIR:   cir.func no_inline alignment(2) internal private dso_local @_ZZ2g3vENK3$_0cvPFiRKiEEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G3]]> {{.*}}) -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>{{.*}}){{.*}} {
 // CIR:   %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_G3]]>>
 // CIR:   %[[RETVAL:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> !s32i>>>
 // CIR:   cir.store %[[THIS_ARG]], %[[THIS_ALLOCA]]
@@ -109,7 +109,7 @@ void use_void_lambda() {
 // CIR:   cir.return
 
 // lambda operator void (*)(int)()
-// CIR: cir.func no_inline internal private dso_local @_ZZ15use_void_lambdavENK3$_0cvPFDaT_EIiEEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_VOID_LAMBDA]]> {{.*}}) -> (!cir.ptr<!cir.func<(!s32i)>>{{.*}})
+// CIR: cir.func no_inline alignment(2) internal private dso_local @_ZZ15use_void_lambdavENK3$_0cvPFDaT_EIiEEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_VOID_LAMBDA]]> {{.*}}) -> (!cir.ptr<!cir.func<(!s32i)>>{{.*}})
 // CIR:   %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_VOID_LAMBDA]]>>
 // CIR:   %[[RETVAL:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!cir.ptr<!cir.func<(!s32i)>>>
 // CIR:   cir.store %[[THIS_ARG]], %[[THIS_ALLOCA]]
@@ -120,7 +120,7 @@ void use_void_lambda() {
 // CIR:   cir.return %[[RET]]
 
 // lambda invoker
-// CIR: cir.func no_inline internal private dso_local @_ZZ2g3vEN3$_08__invokeERKi(%[[REF_I_ARG:.*]]: !cir.ptr<!s32i> {{.*}}) -> (!s32i{{.*}}) {{.*}} {
+// CIR: cir.func no_inline alignment(2) internal private dso_local @_ZZ2g3vEN3$_08__invokeERKi(%[[REF_I_ARG:.*]]: !cir.ptr<!s32i> {{.*}}) -> (!s32i{{.*}}) {{.*}} {
 // CIR:   %[[REF_I_ALLOCA:.*]] = cir.alloca "i" {{.*}} init const
 // CIR:   %[[RETVAL:.*]] = cir.alloca "__retval"
 // CIR:   %[[LAM_ALLOCA:.*]] = cir.alloca "unused.capture" {{.*}} : !cir.ptr<![[REC_LAM_G3]]>
@@ -132,7 +132,7 @@ void use_void_lambda() {
 // CIR:   cir.return %[[RET]]
 
 // lambda operator()
-// CIR: cir.func no_inline lambda internal private dso_local @_ZZ2g3vENK3$_0clERKi(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G3]]> {{.*}}, %[[REF_I_ARG:.*]]: !cir.ptr<!s32i> {{.*}})
+// CIR: cir.func no_inline lambda alignment(2) internal private dso_local @_ZZ2g3vENK3$_0clERKi(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G3]]> {{.*}}, %[[REF_I_ARG:.*]]: !cir.ptr<!s32i> {{.*}})
 // CIR:   %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_G3]]>>
 // CIR:   %[[REF_I_ALLOCA:.*]] = cir.alloca "i" {{.*}} init const
 // CIR:   %[[RETVAL:.*]] = cir.alloca "__retval"
@@ -146,7 +146,7 @@ void use_void_lambda() {
 // CIR:   cir.return %[[RET]]
 
 // lambda invoker
-// CIR: cir.func no_inline internal private dso_local @_ZZ15use_void_lambdavEN3$_08__invokeIiEEDaT_(%[[X_ARG:.*]]: !s32i {{.*}})
+// CIR: cir.func no_inline alignment(2) internal private dso_local @_ZZ15use_void_lambdavEN3$_08__invokeIiEEDaT_(%[[X_ARG:.*]]: !s32i {{.*}})
 // CIR:   %[[X_ALLOCA:.*]] = cir.alloca "x" {{.*}} init : !cir.ptr<!s32i>
 // CIR:   %[[UNUSED_CAPTURE:.*]] = cir.alloca "unused.capture" {{.*}} : !cir.ptr<![[REC_LAM_VOID_LAMBDA]]>
 // CIR:   cir.store %[[X_ARG]], %[[X_ALLOCA]]
@@ -155,7 +155,7 @@ void use_void_lambda() {
 // CIR:   cir.return
 
 // lambda operator()
-// CIR: cir.func no_inline lambda internal private dso_local @_ZZ15use_void_lambdavENK3$_0clIiEEDaT_(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_VOID_LAMBDA]]> {{.*}}, %[[X_ARG:.*]]: !s32i {{.*}})
+// CIR: cir.func no_inline lambda alignment(2) internal private dso_local @_ZZ15use_void_lambdavENK3$_0clIiEEDaT_(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_VOID_LAMBDA]]> {{.*}}, %[[X_ARG:.*]]: !s32i {{.*}})
 // CIR:   %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_VOID_LAMBDA]]>>
 // CIR:   %[[X:.*]] = cir.alloca "x" {{.*}} init : !cir.ptr<!s32i>
 // CIR:   cir.store %[[THIS_ARG]], %[[THIS_ALLOCA]]
diff --git a/clang/test/CIR/CodeGen/lambda.cpp b/clang/test/CIR/CodeGen/lambda.cpp
index bdaaf5f23bfc4..fc5d7c182308d 100644
--- a/clang/test/CIR/CodeGen/lambda.cpp
+++ b/clang/test/CIR/CodeGen/lambda.cpp
@@ -18,7 +18,7 @@ void use_global_lambda() {
 // CIR:   %[[LAMBDA:.*]] = cir.get_global @global_lambda : !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>
 // CIR:   cir.call @_ZNK3$_0clEv(%[[LAMBDA]]) : (!cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> ()
 //
-// CIR: cir.func {{.*}} lambda internal private dso_local @_ZNK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]> {{.*}})
+// CIR: cir.func {{.*}} lambda alignment(2) internal private dso_local @_ZNK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]> {{.*}})
 // CIR:   %[[THIS:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>>
 // CIR:   cir.store %[[THIS_ARG]], %[[THIS]]
 // CIR:   cir.load %[[THIS]]
@@ -50,7 +50,7 @@ void fn() {
 // CIR:   %[[A:.*]] = cir.alloca "a" {{.*}} : !cir.ptr<![[REC_LAM_FN_A:[^>]*]]>
 // CIR:   cir.call @_ZZ2fnvENK3$_0clEv(%[[A]])
 
-// CIR: cir.func {{.*}} lambda internal private dso_local @_ZZ2fnvENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_FN_A]]> {{.*}})
+// CIR: cir.func {{.*}} lambda alignment(2) internal private dso_local @_ZZ2fnvENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_FN_A]]> {{.*}})
 // CIR:   %[[THIS:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_FN_A]]>>
 // CIR:   cir.store %[[THIS_ARG]], %[[THIS]]
 // CIR:   cir.load %[[THIS]]
@@ -93,7 +93,7 @@ void l0() {
 // CIR:   cir.call @_ZZ2l0vENK3$_0clEv(%[[A]])
 // CIR:   cir.return
 
-// CIR: cir.func {{.*}} lambda internal private dso_local @_ZZ2l0vENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_L0_A]]> {{.*}})
+// CIR: cir.func {{.*}} lambda alignment(2) internal private dso_local @_ZZ2l0vENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_L0_A]]> {{.*}})
 // CIR:   %[[THIS_ADDR:.*]] = cir.alloca "this" align(8) init : !cir.ptr<!cir.ptr<![[REC_LAM_L0_A]]>>
 // CIR:   cir.store %[[THIS_ARG]], %[[THIS_ADDR]]
 // CIR:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]]
@@ -257,7 +257,7 @@ int f() {
 // CIR:   %[[RET:.*]] = cir.load{{.*}} %[[RETVAL]]
 // CIR:   cir.return %[[RET]]
 
-// CIR:cir.func {{.*}} lambda internal private dso_local @_ZZ2g2vENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G2]]> {{.*}}) -> (!s32i {llvm.noundef})
+// CIR:cir.func {{.*}} lambda alignment(2) internal private dso_local @_ZZ2g2vENK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_G2]]> {{.*}}) -> (!s32i {llvm.noundef})
 // CIR:   %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_G2]]>>
 // CIR:   %[[RETVAL:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i>
 // CIR:   cir.store %[[THIS_ARG]], %[[THIS_ADDR]]
@@ -425,7 +425,7 @@ struct A {
 // OGCG:   ret i32 %[[LAM_RET]]
 
 // lambda operator() in foo()
-// CIR: cir.func {{.*}} lambda comdat linkonce_odr @_ZZN1A3fooEvENKUlvE_clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_A]]> {{.*}})
+// CIR: cir.func {{.*}} lambda comdat alignment(2) linkonce_odr @_ZZN1A3fooEvENKUlvE_clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_A]]> {{.*}})
 // CIR:   %[[THIS_ADDR:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_A]]>>
 // CIR:   %[[RETVAL:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i>
 // CIR:   cir.store{{.*}} %[[THIS_ARG]], %[[THIS_ADDR]]
diff --git a/clang/test/CIR/CodeGen/static-local.cpp b/clang/test/CIR/CodeGen/static-local.cpp
index 3e1c450d24fb6..f97ee53a909d3 100644
--- a/clang/test/CIR/CodeGen/static-local.cpp
+++ b/clang/test/CIR/CodeGen/static-local.cpp
@@ -386,7 +386,7 @@ struct InMember {
 void InMember::mem_func(int one, int two, int, int three) {
   int some_local = mem_get_int();
   static int magic_static = three + mem_get_int() + one + some_local;
-// CIR-BOTH-LABEL:  cir.func no_inline dso_local @_ZN8InMember8mem_funcEiiii(
+// CIR-BOTH-LABEL:  cir.func no_inline alignment(2) dso_local @_ZN8InMember8mem_funcEiiii(
 // CIR-BOTH:    %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!rec_InMember>>
 // CIR-BOTH:    %[[ONE_ALLOCA:.*]] = cir.alloca "one" {{.*}} init : !cir.ptr<!s32i>
 // CIR-BOTH:    %[[THREE_ALLOCA:.*]] = cir.alloca "three" {{.*}} init : !cir.ptr<!s32i>
@@ -662,7 +662,7 @@ int referenced_inside() {
 // CIR-BOTH:   cir.return %[[RET_LOAD]] : !s32i
 // CIR-BOTH: }
 //
-// CIR-BOTH-LABEL: cir.func no_inline lambda internal private dso_local @_ZZ17referenced_insidevENK3$_0clEv(
+// CIR-BOTH-LABEL: cir.func no_inline lambda alignment(2) internal private dso_local @_ZZ17referenced_insidevENK3$_0clEv(
 // CIR-BOTH:   %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!{{.*}}>>
 // CIR-BOTH:   %[[RET_ALLOCA:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i>
 // CIR-BOTH:   %[[LOAD_THIS:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!{{.*}}>>, !cir.ptr<!{{.*}}>
@@ -723,7 +723,7 @@ int referenced_inside_const() {
 // CIR-BOTH:   cir.return %[[RET_LOAD]] : !s32i
 // CIR-BOTH: }
 //
-// CIR-BOTH-LABEL: cir.func no_inline lambda internal private dso_local @_ZZ23referenced_inside_constvENK3$_0clEv(
+// CIR-BOTH-LABEL: cir.func no_inline lambda alignment(2) internal private dso_local @_ZZ23referenced_inside_constvENK3$_0clEv(
 // CIR-BOTH:   %[[THIS_ALLOCA:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<!{{.*}}>>
 // CIR-BOTH:   %[[RET_ALLOCA:.*]] = cir.alloca "__retval" {{.*}} : !cir.ptr<!s32i>
 // CIR-BOTH:   %[[LOAD_THIS:.*]] = cir.load %[[THIS_ALLOCA]] : !cir.ptr<!cir.ptr<!{{.*}}>>, !cir.ptr<!{{.*}}>
diff --git a/clang/test/CIR/CodeGen/vtable-emission.cpp b/clang/test/CIR/CodeGen/vtable-emission.cpp
index 4c5a6f04719cf..814e88fad6ecb 100644
--- a/clang/test/CIR/CodeGen/vtable-emission.cpp
+++ b/clang/test/CIR/CodeGen/vtable-emission.cpp
@@ -32,4 +32,4 @@ void S::key() {}
 // CHECK: cir.func {{.*}} @_ZN1S3keyEv
 
 // The reference from the vtable should result in nonKey being emitted.
-// CHECK: cir.func no_inline comdat linkonce_odr @_ZN1S6nonKeyEv
+// CHECK: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN1S6nonKeyEv
diff --git a/clang/test/CIR/CodeGen/vtable-thunk-undefined-callee.cpp b/clang/test/CIR/CodeGen/vtable-thunk-undefined-callee.cpp
index 86304f39a4b2d..09b838ccf03e5 100644
--- a/clang/test/CIR/CodeGen/vtable-thunk-undefined-callee.cpp
+++ b/clang/test/CIR/CodeGen/vtable-thunk-undefined-callee.cpp
@@ -27,7 +27,7 @@ int Derived::f(int x) { return x; }
 // Emitting that thunk creates the Right::f declaration on demand.  It must
 // become a sibling of the thunk rather than nesting inside its body, so the
 // declarations are pinned to the ops immediately following the thunk.
-// CIR-LABEL: cir.func available_externally @_ZTv0_n24_N23ConstructionVTableThunk5Right1fEi
+// CIR-LABEL: cir.func alignment(2) available_externally @_ZTv0_n24_N23ConstructionVTableThunk5Right1fEi
 // CIR-NOT:     cir.func
 // CIR:         cir.call @_ZN23ConstructionVTableThunk5Right1fEi(
 // CIR:         cir.return %{{.+}} : !s32i
diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir
index ac74fb8a09355..4169c2136e417 100644
--- a/clang/test/CIR/IR/func.cir
+++ b/clang/test/CIR/IR/func.cir
@@ -205,3 +205,17 @@ cir.func @has_attrs() attributes {foo, baz = 5, floof = "flop"} {
 // CHECK: cir.func @has_attrs() attributes {baz = 5 : i64{{.*}}, floof = "flop", foo} {
 // CHECK:   cir.return
 // CHECK: }
+
+cir.func alignment(3) @has_alignment() {
+  cir.return
+}
+// CHECK: cir.func alignment(3) @has_alignment() {
+// CHECK:   cir.return
+// CHECK: }
+
+cir.func comdat alignment(3) @has_alignment2() {
+  cir.return
+}
+// CHECK: cir.func comdat alignment(3) @has_alignment2() {
+// CHECK:   cir.return
+// CHECK: }
diff --git a/clang/test/CIR/IR/invalid-func.cir b/clang/test/CIR/IR/invalid-func.cir
index c9504bb560c35..e2fde4c5d3f29 100644
--- a/clang/test/CIR/IR/invalid-func.cir
+++ b/clang/test/CIR/IR/invalid-func.cir
@@ -62,3 +62,30 @@ module {
 module {
   cir.func private @l0(!rec_U) // expected-error {{expected all types in the function signature to be CIR types}}
 }
+
+// -----
+
+module {
+  cir.func comdat align(2) @f() { // expected-error {{custom op 'cir.func' expected valid '@'-identifier for symbol name}}
+    cir.return
+}
+
+// -----
+
+module {
+  cir.func comdat alignment(-1) @f() { // expected-error {{custom op 'cir.func' function alignment must be a positive integer}}
+    cir.return
+}
+
+// -----
+
+module {
+  cir.func comdat alignment(0) @f() { // expected-error {{custom op 'cir.func' function alignment must be a positive integer}}
+    cir.return
+}
+// -----
+
+module {
+  cir.func comdat alignment 0 @f() { // expected-error {{expected '('}}
+    cir.return
+}
diff --git a/clang/test/CIR/func-linkage.cpp b/clang/test/CIR/func-linkage.cpp
index c90a69cba105d..55717e39bd418 100644
--- a/clang/test/CIR/func-linkage.cpp
+++ b/clang/test/CIR/func-linkage.cpp
@@ -4,28 +4,24 @@
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
 // RUN: FileCheck %s -check-prefix=LLVM --input-file %t-cir.ll
 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
-// RUN: FileCheck %s -check-prefix=OGCG --input-file %t.ll
+// RUN: FileCheck %s -check-prefix=LLVM --input-file %t.ll
 
 void a() {}
 
 // CIR: cir.func no_inline dso_local @_Z1av()
 // LLVM: define dso_local void @_Z1av()
-// OGCG: define dso_local void @_Z1av()
 
 extern void b();
 // CIR: cir.func private @_Z1bv()
 // LLVM: declare void @_Z1bv()
-// OGCG: declare void @_Z1bv()
 
 static void c() {}
 // CIR: cir.func no_inline internal private dso_local @_ZL1cv()
 // LLVM: define internal void @_ZL1cv()
-// OGCG: define internal void @_ZL1cv()
 
 inline void d() {}
 // CIR: cir.func {{.*}} comdat linkonce_odr @_Z1dv()
-// LLVM: define linkonce_odr void @_Z1dv()
-// OGCG: define linkonce_odr void @_Z1dv(){{.*}} comdat
+// LLVM: define linkonce_odr void @_Z1dv(){{.*}} comdat
 
 namespace {
   void e() {}
@@ -33,12 +29,10 @@ namespace {
 
 // CIR: cir.func {{.*}} internal private dso_local @_ZN12_GLOBAL__N_11eEv()
 // LLVM: define internal void @_ZN12_GLOBAL__N_11eEv()
-// OGCG: define internal void @_ZN12_GLOBAL__N_11eEv()
 
 void f();
 // CIR: cir.func private @_Z1fv()
 // LLVM: declare void @_Z1fv()
-// OGCG: declare void @_Z1fv()
 
 // Force the functions to be emitted
 void reference_funcs() {

>From 6321310e786a4a68fe6bfa556d0252e9dcc800d2 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Tue, 15 Sep 2026 12:44:13 -0700
Subject: [PATCH 2/3] Correctly serialize preferred_alignment, even though it
 isn't properly lowered because the LLVMIR dialect doesn't have it

---
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp      | 27 +++++++++++++++++---
 clang/test/CIR/CodeGen/func-member-attrs.cpp |  2 +-
 clang/test/CIR/IR/func.cir                   |  7 +++++
 clang/test/CIR/IR/invalid-func.cir           | 20 +++++++++++++++
 4 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index ed99bae5e3c78..12291d0d6412b 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2598,6 +2598,8 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
   mlir::StringAttr noProtoNameAttr = getNoProtoAttrName(state.name);
   mlir::StringAttr comdatNameAttr = getComdatAttrName(state.name);
   mlir::StringAttr alignmentNameAttr = getAlignmentAttrName(state.name);
+  mlir::StringAttr preferredAlignmentNameAttr =
+      getPreferredAlignmentAttrName(state.name);
   mlir::StringAttr visNameAttr = getSymVisibilityAttrName(state.name);
   mlir::StringAttr dsoLocalNameAttr = getDsoLocalAttrName(state.name);
   mlir::StringAttr funcInfoNameAttr = getFuncInfoAttrName(state.name);
@@ -2623,19 +2625,33 @@ ParseResult cir::FuncOp::parse(OpAsmParser &parser, OperationState &state) {
   if (parser.parseOptionalKeyword(comdatNameAttr).succeeded())
     state.addAttribute(comdatNameAttr, parser.getBuilder().getUnitAttr());
 
-  if (parser.parseOptionalKeyword(alignmentNameAttr).succeeded()) {
-    int64_t value;
+  auto parseAlignmentBody = [&](int64_t &value) {
     if (parser.parseLParen().failed() || parser.parseInteger(value).failed() ||
         parser.parseRParen().failed())
       return failure();
 
     if (value <= 0)
-      return parser.emitError(loc,
-                              "function alignment must be a positive integer");
+      return static_cast<LogicalResult>(parser.emitError(
+          loc, "function alignment must be a positive integer"));
 
+    return success();
+  };
+
+  if (parser.parseOptionalKeyword(alignmentNameAttr).succeeded()) {
+    int64_t value;
+    if (parseAlignmentBody(value).failed())
+      return failure();
     state.addAttribute(alignmentNameAttr, builder.getI64IntegerAttr(value));
   }
 
+  if (parser.parseOptionalKeyword(preferredAlignmentNameAttr).succeeded()) {
+    int64_t value;
+    if (parseAlignmentBody(value).failed())
+      return failure();
+    state.addAttribute(preferredAlignmentNameAttr,
+                       builder.getI64IntegerAttr(value));
+  }
+
   // Default to external linkage if no keyword is provided.
   state.addAttribute(getLinkageAttrNameString(),
                      GlobalLinkageKindAttr::get(
@@ -2950,6 +2966,9 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
   if (getAlignment())
     p << " alignment(" << *getAlignment() << ')';
 
+  if (getPreferredAlignment())
+    p << " preferred_alignment(" << *getPreferredAlignment() << ')';
+
   if (getLinkage() != GlobalLinkageKind::ExternalLinkage)
     p << ' ' << stringifyGlobalLinkageKind(getLinkage());
 
diff --git a/clang/test/CIR/CodeGen/func-member-attrs.cpp b/clang/test/CIR/CodeGen/func-member-attrs.cpp
index b62c46d7591a1..f8228607507db 100644
--- a/clang/test/CIR/CodeGen/func-member-attrs.cpp
+++ b/clang/test/CIR/CodeGen/func-member-attrs.cpp
@@ -33,7 +33,7 @@ void use(S &s) {
 // CIR: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN1S13inline_methodEv(
 // LLVM: define linkonce_odr void @_ZN1S13inline_methodEv({{.*}}) #{{[0-9]+}} comdat align 2
 
-// CIR: cir.func no_inline comdat alignment(8) linkonce_odr @_ZN1S14aligned_methodEv(
+// CIR: cir.func no_inline comdat alignment(8) preferred_alignment(8) linkonce_odr @_ZN1S14aligned_methodEv(
 // LLVM: define linkonce_odr void @_ZN1S14aligned_methodEv({{.*}}) #{{[0-9]+}} comdat align 8
 
 // CIR: cir.func no_inline comdat alignment(2) linkonce_odr @_ZN1S13static_methodEv(
diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir
index 4169c2136e417..591c397e24e8c 100644
--- a/clang/test/CIR/IR/func.cir
+++ b/clang/test/CIR/IR/func.cir
@@ -219,3 +219,10 @@ cir.func comdat alignment(3) @has_alignment2() {
 // CHECK: cir.func comdat alignment(3) @has_alignment2() {
 // CHECK:   cir.return
 // CHECK: }
+
+cir.func comdat alignment(3) preferred_alignment(4) @has_alignment3() {
+  cir.return
+}
+// CHECK: cir.func comdat alignment(3) preferred_alignment(4) @has_alignment3() {
+// CHECK:   cir.return
+// CHECK: }
diff --git a/clang/test/CIR/IR/invalid-func.cir b/clang/test/CIR/IR/invalid-func.cir
index e2fde4c5d3f29..c4b0eb1fd0c3e 100644
--- a/clang/test/CIR/IR/invalid-func.cir
+++ b/clang/test/CIR/IR/invalid-func.cir
@@ -89,3 +89,23 @@ module {
   cir.func comdat alignment 0 @f() { // expected-error {{expected '('}}
     cir.return
 }
+
+// -----
+
+module {
+  cir.func comdat preferred_alignment(-1) @f() { // expected-error {{custom op 'cir.func' function alignment must be a positive integer}}
+    cir.return
+}
+
+// -----
+
+module {
+  cir.func comdat preferred_alignment(0) @f() { // expected-error {{custom op 'cir.func' function alignment must be a positive integer}}
+    cir.return
+}
+// -----
+
+module {
+  cir.func comdat preferred_alignment 0 @f() { // expected-error {{expected '('}}
+    cir.return
+}

>From efe62a0b672b49f73936d01e9c8e8f6d04f665a6 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Thu, 17 Sep 2026 16:42:30 -0700
Subject: [PATCH 3/3] Merge comdat data variables

---
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp   | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 13dab4c9e26d3..62da1ac25c9fa 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3152,9 +3152,17 @@ mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
 static mlir::SymbolRefAttr getComdatAttrHelper(mlir::ModuleOp modOp,
                                                mlir::OpBuilder &builder,
                                                StringRef symName,
-                                               mlir::LLVM::ComdatOp &comdatOp,
-                                               StringRef comdatName) {
+                                               mlir::LLVM::ComdatOp &comdatOp) {
   mlir::OpBuilder::InsertionGuard guard(builder);
+  StringRef comdatName = "__llvm_comdat";
+  if (!comdatOp) {
+    // The GlobalOp and FuncOp lowering patterns each cache their own
+    // 'comdatOp', but both now share a single module-level comdat region, so
+    // whichever pattern gets here second has to find the existing one rather
+    // than create a duplicate symbol.
+    comdatOp = modOp.lookupSymbol<mlir::LLVM::ComdatOp>(comdatName);
+  }
+
   if (!comdatOp) {
     builder.setInsertionPointToStart(modOp.getBody());
     comdatOp =
@@ -3183,8 +3191,7 @@ CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
   if (!op.getComdat())
     return mlir::SymbolRefAttr{};
   return getComdatAttrHelper(op->getParentOfType<mlir::ModuleOp>(), builder,
-                             op.getSymName(), comdatOp,
-                             "__llvm_comdat_globals");
+                             op.getSymName(), comdatOp);
 }
 
 mlir::SymbolRefAttr
@@ -3193,7 +3200,7 @@ CIRToLLVMFuncOpLowering::getComdatAttr(cir::FuncOp &op,
   if (!op.getComdat())
     return mlir::SymbolRefAttr{};
   return getComdatAttrHelper(op->getParentOfType<mlir::ModuleOp>(), builder,
-                             op.getSymName(), comdatOp, "__llvm_comdat_funcs");
+                             op.getSymName(), comdatOp);
 }
 
 mlir::LogicalResult CIRToLLVMSwitchFlatOpLowering::matchAndRewrite(



More information about the cfe-commits mailing list