Skip to content

Conversation

@andykaylor
Copy link
Contributor

This adds handling for records with trivial constructors in CIR's ConstExprEmitter.

This adds handling for records with trivial constructors in CIR's
ConstExprEmitter.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Oct 23, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 23, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

Changes

This adds handling for records with trivial constructors in CIR's ConstExprEmitter.


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

3 Files Affected:

  • (modified) clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp (+1-2)
  • (modified) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+21-3)
  • (added) clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp (+44)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index d6d226b3f75db..8fe0d9b4a69ef 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -362,8 +362,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
     cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCXXTypeidExpr");
   }
   void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(),
-                     "AggExprEmitter: VisitMaterializeTemporaryExpr");
+    Visit(e->getSubExpr());
   }
   void VisitOpaqueValueExpr(OpaqueValueExpr *e) {
     cgf.cgm.errorNYI(e->getSourceRange(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 800262aac8fa4..c1f4fe5973d17 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -1069,9 +1069,27 @@ class ConstExprEmitter
 
   mlir::Attribute VisitCXXConstructExpr(CXXConstructExpr *e, QualType ty) {
     if (!e->getConstructor()->isTrivial())
-      return nullptr;
-    cgm.errorNYI(e->getBeginLoc(), "trivial constructor const handling");
-    return {};
+      return {};
+
+    // Only default and copy/move constructors can be trivial.
+    if (e->getNumArgs()) {
+      assert(e->getNumArgs() == 1 && "trivial ctor with > 1 argument");
+      assert(e->getConstructor()->isCopyOrMoveConstructor() &&
+             "trivial ctor has argument but isn't a copy/move ctor");
+
+      Expr *arg = e->getArg(0);
+      assert(cgm.getASTContext().hasSameUnqualifiedType(ty, arg->getType()) &&
+             "argument to copy ctor is of wrong type");
+
+      // Look through the temporary; it's just converting the value to an lvalue
+      // to pass it to the constructor.
+      if (auto const *mte = dyn_cast<MaterializeTemporaryExpr>(arg))
+        return Visit(mte->getSubExpr(), ty);
+      // Don't try to support arbitrary lvalue-to-rvalue conversions for now.
+      return {};
+    }
+
+    return cgm.getBuilder().getZeroInitAttr(cgm.convertType(ty));
   }
 
   mlir::Attribute VisitStringLiteral(StringLiteral *e, QualType t) {
diff --git a/clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp b/clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp
new file mode 100644
index 0000000000000..7429549100362
--- /dev/null
+++ b/clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++11 -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+struct StructWithDefaultCtor {
+  int n;
+};
+
+StructWithDefaultCtor defCtor = StructWithDefaultCtor();
+
+// CIR: cir.global {{.*}} @defCtor = #cir.zero : !rec_StructWithDefaultCtor
+// LLVM: @defCtor = global %struct.StructWithDefaultCtor zeroinitializer
+// OGCG: @defCtor = global %struct.StructWithDefaultCtor zeroinitializer
+
+struct StructWithCtorArg {
+  double value;
+  StructWithCtorArg(const double& x) : value(x) {}
+};
+
+StructWithCtorArg withArg = 0.0;
+
+// CIR: cir.global {{.*}} @withArg = #cir.zero : !rec_StructWithCtorArg
+// LLVM: @withArg = global %struct.StructWithCtorArg zeroinitializer
+// OGCG: @withArg = global %struct.StructWithCtorArg zeroinitializer
+
+// CIR: cir.func {{.*}} @__cxx_global_var_init()
+// CIR:   %[[TMP0:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["ref.tmp0"]
+// CIR:   %[[WITH_ARG:.*]] = cir.get_global @withArg : !cir.ptr<!rec_StructWithCtorArg>
+// CIR:   %[[ZERO:.*]] = cir.const #cir.fp<0.000000e+00> : !cir.double
+// CIR:   cir.store{{.*}} %[[ZERO]], %[[TMP0]] : !cir.double, !cir.ptr<!cir.double>
+// CIR:   cir.call @_ZN17StructWithCtorArgC1ERKd(%[[WITH_ARG]], %[[TMP0]]) : (!cir.ptr<!rec_StructWithCtorArg>, !cir.ptr<!cir.double>) -> ()
+
+// LLVM: define {{.*}} void @__cxx_global_var_init()
+// LLVM:   %[[TMP0:.*]] = alloca double
+// LLVM:   store double 0.000000e+00, ptr %[[TMP0]]
+// LLVM:   call void @_ZN17StructWithCtorArgC1ERKd(ptr @withArg, ptr %[[TMP0]])
+
+// OGCG: define {{.*}} void @__cxx_global_var_init()
+// OGCG:   %[[TMP0:.*]] = alloca double
+// OGCG:   store double 0.000000e+00, ptr %[[TMP0]]
+// OGCG:   call void @_ZN17StructWithCtorArgC1ERKd(ptr {{.*}} @withArg, ptr {{.*}} %[[TMP0]])

// to pass it to the constructor.
if (auto const *mte = dyn_cast<MaterializeTemporaryExpr>(arg))
return Visit(mte->getSubExpr(), ty);
// Don't try to support arbitrary lvalue-to-rvalue conversions for now.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should this be a not-yet-implemented or something?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This comment is carried over from classic codegen, which does the same thing here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh weird... Can you replace the dyn_cast with a cast in classic codegen? Does it hit anything? I'm actually pretty concerned by this...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I report and error after the dyn_cast falls through in classic codegen, it fires 14 times in eight tests in the CodeGenCXX tests:

GUID thing = (side_effect(), __uuidof(Curly));


(void) new std::initializer_list<int> {i, 2, 3};

(void) new std::initializer_list<destroyme1> {destroyme1(), destroyme1()};


auto x = std::initializer_list<S>{ S(), S() };

SelfReference self_reference_2 = {self_reference_1};


static auto [x1, x2] = make<A>();

static auto [x1, x2] = make<B>();

X2< B > bg = X1< X2< B > >::get();

I haven't looked at these in detail yet, but I know that we can get here if the constructor argument is not a constant, and if we do then returning nullptr is the correct behavior.

The only case that would be a problem is if the argument is not a MaterializeTemporaryExpr but is a constant, and even in that case if we return null here, something else is going to handle it, though possibly not correctly.

I also tried inserting a call to ConstantExprEmitter::Visit(Arg, Ty) in the case where the dyn_cast failed, and that doesn't change anything.

Copy link
Member

Choose a reason for hiding this comment

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

One possible alternative is to add a missing feature as a way to track what needs to be investigated into both CodeGen paths.

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.

LGTM once Erich is happy

// to pass it to the constructor.
if (auto const *mte = dyn_cast<MaterializeTemporaryExpr>(arg))
return Visit(mte->getSubExpr(), ty);
// Don't try to support arbitrary lvalue-to-rvalue conversions for now.
Copy link
Member

Choose a reason for hiding this comment

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

One possible alternative is to add a missing feature as a way to track what needs to be investigated into both CodeGen paths.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

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

The TODO + missing-features is good enough here, the fact that upstream survived with this being this way for this long means it is probably not pressing. But please create an issue for someone to investigate, obviously minor priority.

@andykaylor
Copy link
Contributor Author

But please create an issue for someone to investigate, obviously minor priority.

#165017

@andykaylor andykaylor merged commit ab1765d into llvm:main Oct 24, 2025
10 checks passed
@andykaylor andykaylor deleted the cir-trivial-ctor-const branch October 24, 2025 17:33
dvbuka pushed a commit to dvbuka/llvm-project that referenced this pull request Oct 27, 2025
This adds handling for records with trivial constructors in CIR's
ConstExprEmitter.
Lukacma pushed a commit to Lukacma/llvm-project that referenced this pull request Oct 29, 2025
This adds handling for records with trivial constructors in CIR's
ConstExprEmitter.
aokblast pushed a commit to aokblast/llvm-project that referenced this pull request Oct 30, 2025
This adds handling for records with trivial constructors in CIR's
ConstExprEmitter.
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