From 7c2568a42968e315867fceb8e5a928fc9ee157a4 Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Wed, 22 Oct 2025 09:21:27 -0700 Subject: [PATCH 1/2] [CIR] Upstream trivial constructor const handling This adds handling for records with trivial constructors in CIR's ConstExprEmitter. --- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 24 ++++++++-- .../CIR/CodeGen/trivial-ctor-const-init.cpp | 44 +++++++++++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp 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 { 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(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, ["ref.tmp0"] +// CIR: %[[WITH_ARG:.*]] = cir.get_global @withArg : !cir.ptr +// CIR: %[[ZERO:.*]] = cir.const #cir.fp<0.000000e+00> : !cir.double +// CIR: cir.store{{.*}} %[[ZERO]], %[[TMP0]] : !cir.double, !cir.ptr +// CIR: cir.call @_ZN17StructWithCtorArgC1ERKd(%[[WITH_ARG]], %[[TMP0]]) : (!cir.ptr, !cir.ptr) -> () + +// 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]]) From 20b47cdc088a4f116729b5dfa8eff2d5eabdcdfb Mon Sep 17 00:00:00 2001 From: Andy Kaylor Date: Fri, 24 Oct 2025 09:41:57 -0700 Subject: [PATCH 2/2] Add marker for future investigation --- clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 598e826a473a6..d5bc5ce8d8997 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -231,6 +231,7 @@ struct MissingFeatures { static bool coverageMapping() { return false; } static bool createInvariantGroup() { return false; } static bool createProfileWeightsForLoop() { return false; } + static bool ctorConstLvalueToRvalueConversion() { return false; } static bool ctorMemcpyizer() { return false; } static bool cudaSupport() { return false; } static bool cxxRecordStaticMembers() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index c1f4fe5973d17..8fc36aea93a3d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -1085,6 +1085,11 @@ class ConstExprEmitter // to pass it to the constructor. if (auto const *mte = dyn_cast(arg)) return Visit(mte->getSubExpr(), ty); + + // TODO: Investigate whether there are cases that can fall through to here + // that need to be handled. This is missing in classic codegen also. + assert(!cir::MissingFeatures::ctorConstLvalueToRvalueConversion()); + // Don't try to support arbitrary lvalue-to-rvalue conversions for now. return {}; }