[clang] [CIR] Inline trivial copy/move assignment at call sites (PR #198918)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 22 13:47:14 PDT 2026
================
@@ -901,6 +901,36 @@ void CIRGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &args) {
assert(!cir::MissingFeatures::incrementProfileCounter());
assert(!cir::MissingFeatures::runCleanupsScope());
+ // For a memcpy-equivalent assignment operator, copy the whole object, then
+ // fall through to emit the trailing `return *this`.
+ if (assignOp->isMemcpyEquivalentSpecialMember(getContext())) {
+ CanQualType recordTy =
+ getContext().getCanonicalTagType(assignOp->getParent());
+ LValue dest = makeNaturalAlignAddrLValue(loadCXXThis(), recordTy);
+ mlir::Value srcPtr = builder.createLoad(getLoc(assignOp->getLocation()),
+ getAddrOfLocalVar(args.back()));
+ LValue src = makeNaturalAlignAddrLValue(srcPtr, recordTy);
+ emitAggregateAssign(dest, src, recordTy);
----------------
adams381 wrote:
This block is really doing two separate jobs, and only one of them needs the frontend.
For a trivially-copyable struct the AST already carries the work: per-field assignment expressions, plus a `__builtin_memcpy` call for array members. Dropping the whole-object copy and just emitting the body (which the non-memcpy path further down already does) is exactly the "reflect the AST and let optimization coalesce" behavior. The `assert(!MissingFeatures::assignMemcpyizer())` there is only the coalescing step; the per-field stores stand on their own and survive `-O3`. That case needs no frontend change.
The union is the one that can't go through that path. Its body is empty -- just `return *this` -- because Sema skips union fields at the FIXME in `SemaDeclCXX.cpp:15474` (copy) and `:15863` (move). There are no copies to leave, and a memcpyizer has nothing to coalesce on an empty body. That empty body is the original regression here: `*++yyvsp = yylval` in `kc`, where `YYSTYPE` is a union, whose no-op `operator=` LLVM deletes at `-O3`. Only a real AST node or this synthesis fills it.
So two ways forward, and I'd like your and @andykaylor's call before touching shared code (same scope question I raised on the other thread Jun 16):
1. Implement the FIXME -- form an AST node for the implied union memcpy in copy and move assignment. That fixes classic and CIR together and lets this PR drop the synthesis. It is a shared-AST change: it shifts classic union copy/move codegen, likely reaches constexpr handling, and updates existing CodeGen tests, and the FIXME has been open a long time, so I don't want to start it without a maintainer nod.
2. If that's more than this PR should carry, narrow it to the struct/array lowering and `errorNYI` the union body, then handle the union in its own PR once the AST question is settled.
Which do you prefer?
https://github.com/llvm/llvm-project/pull/198918
More information about the cfe-commits
mailing list