Skip to content

Commit 8c716be

Browse files
authored
[CIR] Emit copy for aggregate initialization (#155697)
This adds the implementation of aggEmitFinalDestCopy for the case where the destination value is not ignored. This requires adding the cir.copy operation and associated interface code.
1 parent be616b4 commit 8c716be

File tree

15 files changed

+300
-22
lines changed

15 files changed

+300
-22
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
245245
return createGetGlobal(global.getLoc(), global);
246246
}
247247

248+
/// Create a copy with inferred length.
249+
cir::CopyOp createCopy(mlir::Value dst, mlir::Value src) {
250+
return cir::CopyOp::create(*this, dst.getLoc(), dst, src);
251+
}
252+
248253
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, mlir::Value dst,
249254
bool isVolatile = false,
250255
mlir::IntegerAttr align = {},

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,52 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
24512451
];
24522452
}
24532453

2454+
//===----------------------------------------------------------------------===//
2455+
// CopyOp
2456+
//===----------------------------------------------------------------------===//
2457+
2458+
def CIR_CopyOp : CIR_Op<"copy",[
2459+
SameTypeOperands,
2460+
DeclareOpInterfaceMethods<PromotableMemOpInterface>
2461+
]> {
2462+
let summary = "Copies contents from a CIR pointer to another";
2463+
let description = [{
2464+
Given two CIR pointers, `src` and `dst`, `cir.copy` will copy the memory
2465+
pointed by `src` to the memory pointed by `dst`.
2466+
2467+
The number of bytes copied is inferred from the pointee type. The pointee
2468+
type of `src` and `dst` must match and both must implement the
2469+
`DataLayoutTypeInterface`.
2470+
2471+
Examples:
2472+
2473+
```mlir
2474+
// Copying contents from one record to another:
2475+
cir.copy %0 to %1 : !cir.ptr<!record_ty>
2476+
```
2477+
}];
2478+
2479+
let arguments = (ins
2480+
Arg<CIR_PointerType, "", [MemWrite]>:$dst,
2481+
Arg<CIR_PointerType, "", [MemRead]>:$src
2482+
);
2483+
2484+
let assemblyFormat = [{$src `to` $dst
2485+
attr-dict `:` qualified(type($dst))
2486+
}];
2487+
let hasVerifier = 1;
2488+
2489+
let extraClassDeclaration = [{
2490+
/// Returns the pointer type being copied.
2491+
cir::PointerType getType() { return getSrc().getType(); }
2492+
2493+
/// Returns the number of bytes to be copied.
2494+
unsigned getLength(const mlir::DataLayout &dt) {
2495+
return dt.getTypeSize(getType().getPointee());
2496+
}
2497+
}];
2498+
}
2499+
24542500
//===----------------------------------------------------------------------===//
24552501
// ReturnAddrOp and FrameAddrOp
24562502
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ struct MissingFeatures {
168168
// Misc
169169
static bool abiArgInfo() { return false; }
170170
static bool addHeapAllocSiteMetadata() { return false; }
171+
static bool aggEmitFinalDestCopyRValue() { return false; }
171172
static bool aggValueSlot() { return false; }
172173
static bool aggValueSlotAlias() { return false; }
173174
static bool aggValueSlotDestructedFlag() { return false; }

clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp

Lines changed: 94 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,19 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
6262
/// Perform the final copy to DestPtr, if desired.
6363
void emitFinalDestCopy(QualType type, const LValue &src);
6464

65+
void emitCopy(QualType type, const AggValueSlot &dest,
66+
const AggValueSlot &src);
67+
6568
void emitInitializationToLValue(Expr *e, LValue lv);
6669

6770
void emitNullInitializationToLValue(mlir::Location loc, LValue lv);
6871

6972
void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }
7073

74+
void VisitArraySubscriptExpr(ArraySubscriptExpr *e) {
75+
emitAggLoadOfLValue(e);
76+
}
77+
7178
void VisitCallExpr(const CallExpr *e);
7279
void VisitStmtExpr(const StmtExpr *e) {
7380
CIRGenFunction::StmtExprEvaluation eval(cgf);
@@ -91,13 +98,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
9198
}
9299

93100
// Stubs -- These should be moved up when they are implemented.
94-
void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *e) {
95-
// We shouldn't really get here, but we do because of missing handling for
96-
// emitting constant aggregate initializers. If we just ignore this, a
97-
// fallback handler will do the right thing.
98-
assert(!cir::MissingFeatures::constEmitterAggILE());
99-
return;
100-
}
101101
void VisitCastExpr(CastExpr *e) {
102102
switch (e->getCastKind()) {
103103
case CK_LValueToRValue:
@@ -167,10 +167,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
167167
cgf.cgm.errorNYI(e->getSourceRange(),
168168
"AggExprEmitter: VisitCompoundLiteralExpr");
169169
}
170-
void VisitArraySubscriptExpr(ArraySubscriptExpr *e) {
171-
cgf.cgm.errorNYI(e->getSourceRange(),
172-
"AggExprEmitter: VisitArraySubscriptExpr");
173-
}
174170
void VisitPredefinedExpr(const PredefinedExpr *e) {
175171
cgf.cgm.errorNYI(e->getSourceRange(),
176172
"AggExprEmitter: VisitPredefinedExpr");
@@ -460,7 +456,31 @@ void AggExprEmitter::emitFinalDestCopy(QualType type, const LValue &src) {
460456
if (dest.isIgnored())
461457
return;
462458

463-
cgf.cgm.errorNYI("emitFinalDestCopy: non-ignored dest is NYI");
459+
assert(!cir::MissingFeatures::aggValueSlotVolatile());
460+
assert(!cir::MissingFeatures::aggEmitFinalDestCopyRValue());
461+
assert(!cir::MissingFeatures::aggValueSlotGC());
462+
463+
AggValueSlot srcAgg = AggValueSlot::forLValue(src, AggValueSlot::IsDestructed,
464+
AggValueSlot::IsAliased,
465+
AggValueSlot::MayOverlap);
466+
emitCopy(type, dest, srcAgg);
467+
}
468+
469+
/// Perform a copy from the source into the destination.
470+
///
471+
/// \param type - the type of the aggregate being copied; qualifiers are
472+
/// ignored
473+
void AggExprEmitter::emitCopy(QualType type, const AggValueSlot &dest,
474+
const AggValueSlot &src) {
475+
assert(!cir::MissingFeatures::aggValueSlotGC());
476+
477+
// If the result of the assignment is used, copy the LHS there also.
478+
// It's volatile if either side is. Use the minimum alignment of
479+
// the two sides.
480+
LValue destLV = cgf.makeAddrLValue(dest.getAddress(), type);
481+
LValue srcLV = cgf.makeAddrLValue(src.getAddress(), type);
482+
assert(!cir::MissingFeatures::aggValueSlotVolatile());
483+
cgf.emitAggregateCopy(destLV, srcLV, type, dest.mayOverlap());
464484
}
465485

466486
void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) {
@@ -712,6 +732,68 @@ void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) {
712732
AggExprEmitter(*this, slot).Visit(const_cast<Expr *>(e));
713733
}
714734

735+
void CIRGenFunction::emitAggregateCopy(LValue dest, LValue src, QualType ty,
736+
AggValueSlot::Overlap_t mayOverlap) {
737+
// TODO(cir): this function needs improvements, commented code for now since
738+
// this will be touched again soon.
739+
assert(!ty->isAnyComplexType() && "Unexpected copy of complex");
740+
741+
Address destPtr = dest.getAddress();
742+
Address srcPtr = src.getAddress();
743+
744+
if (getLangOpts().CPlusPlus) {
745+
if (auto *record = ty->getAsCXXRecordDecl()) {
746+
assert((record->hasTrivialCopyConstructor() ||
747+
record->hasTrivialCopyAssignment() ||
748+
record->hasTrivialMoveConstructor() ||
749+
record->hasTrivialMoveAssignment() ||
750+
record->hasAttr<TrivialABIAttr>() || record->isUnion()) &&
751+
"Trying to aggregate-copy a type without a trivial copy/move "
752+
"constructor or assignment operator");
753+
// Ignore empty classes in C++.
754+
if (record->isEmpty())
755+
return;
756+
}
757+
}
758+
759+
assert(!cir::MissingFeatures::cudaSupport());
760+
761+
// Aggregate assignment turns into llvm.memcpy. This is almost valid per
762+
// C99 6.5.16.1p3, which states "If the value being stored in an object is
763+
// read from another object that overlaps in anyway the storage of the first
764+
// object, then the overlap shall be exact and the two objects shall have
765+
// qualified or unqualified versions of a compatible type."
766+
//
767+
// memcpy is not defined if the source and destination pointers are exactly
768+
// equal, but other compilers do this optimization, and almost every memcpy
769+
// implementation handles this case safely. If there is a libc that does not
770+
// safely handle this, we can add a target hook.
771+
772+
// Get data size info for this aggregate. Don't copy the tail padding if this
773+
// might be a potentially-overlapping subobject, since the tail padding might
774+
// be occupied by a different object. Otherwise, copying it is fine.
775+
TypeInfoChars typeInfo;
776+
if (mayOverlap)
777+
typeInfo = getContext().getTypeInfoDataSizeInChars(ty);
778+
else
779+
typeInfo = getContext().getTypeInfoInChars(ty);
780+
781+
assert(!cir::MissingFeatures::aggValueSlotVolatile());
782+
783+
// NOTE(cir): original codegen would normally convert destPtr and srcPtr to
784+
// i8* since memcpy operates on bytes. We don't need that in CIR because
785+
// cir.copy will operate on any CIR pointer that points to a sized type.
786+
787+
// Don't do any of the memmove_collectable tests if GC isn't set.
788+
if (cgm.getLangOpts().getGC() != LangOptions::NonGC)
789+
cgm.errorNYI("emitAggregateCopy: GC");
790+
791+
[[maybe_unused]] cir::CopyOp copyOp =
792+
builder.createCopy(destPtr.getPointer(), srcPtr.getPointer());
793+
794+
assert(!cir::MissingFeatures::opTBAA());
795+
}
796+
715797
LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) {
716798
assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!");
717799
Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange()));

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,16 @@ class CIRGenFunction : public CIRGenTypeCache {
965965

966966
LValue emitAggExprToLValue(const Expr *e);
967967

968+
/// Emit an aggregate copy.
969+
///
970+
/// \param isVolatile \c true iff either the source or the destination is
971+
/// volatile.
972+
/// \param MayOverlap Whether the tail padding of the destination might be
973+
/// occupied by some other object. More efficient code can often be
974+
/// generated if not.
975+
void emitAggregateCopy(LValue dest, LValue src, QualType eltTy,
976+
AggValueSlot::Overlap_t mayOverlap);
977+
968978
/// Emit code to compute the specified expression which can have any type. The
969979
/// result is returned as an RValue struct. If this is an aggregate
970980
/// expression, the aggloc/agglocvolatile arguments indicate where the result

clang/lib/CIR/CodeGen/CIRGenValue.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,8 @@ class AggValueSlot {
379379

380380
mlir::Value getPointer() const { return addr.getPointer(); }
381381

382+
Overlap_t mayOverlap() const { return Overlap_t(overlapFlag); }
383+
382384
IsZeroed_t isZeroed() const { return IsZeroed_t(zeroedFlag); }
383385

384386
RValue asRValue() const {

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,21 @@ OpFoldResult cir::UnaryOp::fold(FoldAdaptor adaptor) {
19191919
return {};
19201920
}
19211921

1922+
//===----------------------------------------------------------------------===//
1923+
// CopyOp Definitions
1924+
//===----------------------------------------------------------------------===//
1925+
1926+
LogicalResult cir::CopyOp::verify() {
1927+
// A data layout is required for us to know the number of bytes to be copied.
1928+
if (!getType().getPointee().hasTrait<DataLayoutTypeInterface::Trait>())
1929+
return emitError() << "missing data layout for pointee type";
1930+
1931+
if (getSrc() == getDst())
1932+
return emitError() << "source and destination are the same";
1933+
1934+
return mlir::success();
1935+
}
1936+
19221937
//===----------------------------------------------------------------------===//
19231938
// GetMemberOp Definitions
19241939
//===----------------------------------------------------------------------===//

clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,45 @@ DeletionKind cir::StoreOp::removeBlockingUses(
118118
return DeletionKind::Delete;
119119
}
120120

121+
//===----------------------------------------------------------------------===//
122+
// Interfaces for CopyOp
123+
//===----------------------------------------------------------------------===//
124+
125+
bool cir::CopyOp::loadsFrom(const MemorySlot &slot) {
126+
return getSrc() == slot.ptr;
127+
}
128+
129+
bool cir::CopyOp::storesTo(const MemorySlot &slot) {
130+
return getDst() == slot.ptr;
131+
}
132+
133+
Value cir::CopyOp::getStored(const MemorySlot &slot, OpBuilder &builder,
134+
Value reachingDef, const DataLayout &dataLayout) {
135+
return cir::LoadOp::create(builder, getLoc(), slot.elemType, getSrc());
136+
}
137+
138+
DeletionKind cir::CopyOp::removeBlockingUses(
139+
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
140+
OpBuilder &builder, mlir::Value reachingDefinition,
141+
const DataLayout &dataLayout) {
142+
if (loadsFrom(slot))
143+
cir::StoreOp::create(builder, getLoc(), reachingDefinition, getDst(),
144+
/*isVolatile=*/false,
145+
/*alignment=*/mlir::IntegerAttr{},
146+
/*mem-order=*/cir::MemOrderAttr());
147+
return DeletionKind::Delete;
148+
}
149+
150+
bool cir::CopyOp::canUsesBeRemoved(
151+
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
152+
SmallVectorImpl<OpOperand *> &newBlockingUses,
153+
const DataLayout &dataLayout) {
154+
if (getDst() == getSrc())
155+
return false;
156+
157+
return getLength(dataLayout) == dataLayout.getTypeSize(slot.elemType);
158+
}
159+
121160
//===----------------------------------------------------------------------===//
122161
// Interfaces for CastOp
123162
//===----------------------------------------------------------------------===//

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
173173
llvm_unreachable("Unknown CIR linkage type");
174174
}
175175

176+
mlir::LogicalResult CIRToLLVMCopyOpLowering::matchAndRewrite(
177+
cir::CopyOp op, OpAdaptor adaptor,
178+
mlir::ConversionPatternRewriter &rewriter) const {
179+
mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
180+
const mlir::Value length = mlir::LLVM::ConstantOp::create(
181+
rewriter, op.getLoc(), rewriter.getI32Type(), op.getLength(layout));
182+
assert(!cir::MissingFeatures::aggValueSlotVolatile());
183+
rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
184+
op, adaptor.getDst(), adaptor.getSrc(), length, /*isVolatile=*/false);
185+
return mlir::success();
186+
}
187+
176188
static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter,
177189
mlir::Value llvmSrc, mlir::Type llvmDstIntTy,
178190
bool isUnsigned, uint64_t cirSrcWidth,
@@ -2430,6 +2442,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
24302442
CIRToLLVMComplexRealOpLowering,
24312443
CIRToLLVMComplexRealPtrOpLowering,
24322444
CIRToLLVMComplexSubOpLowering,
2445+
CIRToLLVMCopyOpLowering,
24332446
CIRToLLVMConstantOpLowering,
24342447
CIRToLLVMExpectOpLowering,
24352448
CIRToLLVMFAbsOpLowering,

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,15 @@ class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> {
170170
mlir::ConversionPatternRewriter &) const override;
171171
};
172172

173+
class CIRToLLVMCopyOpLowering : public mlir::OpConversionPattern<cir::CopyOp> {
174+
public:
175+
using mlir::OpConversionPattern<cir::CopyOp>::OpConversionPattern;
176+
177+
mlir::LogicalResult
178+
matchAndRewrite(cir::CopyOp op, OpAdaptor,
179+
mlir::ConversionPatternRewriter &) const override;
180+
};
181+
173182
class CIRToLLVMExpectOpLowering
174183
: public mlir::OpConversionPattern<cir::ExpectOp> {
175184
public:

0 commit comments

Comments
 (0)