-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[CIR] Upstream trivial constructor const handling #164849
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
Conversation
This adds handling for records with trivial constructors in CIR's ConstExprEmitter.
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis 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:
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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...
There was a problem hiding this comment.
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)); |
| target({ inner, { destroyme1() } }); |
| (void) new std::initializer_list<int> {i, 2, 3}; |
| (void) new std::initializer_list<destroyme1> {destroyme1(), destroyme1()}; |
| std::initializer_list<S>{ S(), S() }; |
| auto x = std::initializer_list<S>{ S(), S() }; |
| Y y2 = { x, z }; |
| SelfReference self_reference_2 = {self_reference_1}; |
| A<int> A<int>::instance = bar(); |
| auto [a1, a2] = make<A>(); |
| static auto [x1, x2] = make<A>(); |
| static auto [x1, x2] = make<B>(); |
| X2< B > bg = X1< X2< B > >::get(); |
| PR23373 pr23373_b(pr23373_a); |
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.
There was a problem hiding this comment.
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.
bcardosolopes
left a comment
There was a problem hiding this 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. |
There was a problem hiding this comment.
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.
erichkeane
left a comment
There was a problem hiding this 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.
|
This adds handling for records with trivial constructors in CIR's ConstExprEmitter.
This adds handling for records with trivial constructors in CIR's ConstExprEmitter.
This adds handling for records with trivial constructors in CIR's ConstExprEmitter.
This adds handling for records with trivial constructors in CIR's ConstExprEmitter.