Skip to content

Commit 1eab5f0

Browse files
committed
[CIR] Emit copy for aggregate initialization
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 96b1dfb commit 1eab5f0

File tree

15 files changed

+297
-22
lines changed

15 files changed

+297
-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: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2451,6 +2451,51 @@ 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 arguments = (ins
2463+
Arg<CIR_PointerType, "", [MemWrite]>:$dst,
2464+
Arg<CIR_PointerType, "", [MemRead]>:$src
2465+
);
2466+
let summary = "Copies contents from a CIR pointer to another";
2467+
let description = [{
2468+
Given two CIR pointers, `src` and `dst`, `cir.copy` will copy the memory
2469+
pointed by `src` to the memory pointed by `dst`.
2470+
2471+
The number of bytes copied is inferred from the pointee type. The pointee
2472+
type of `src` and `dst` must match and both must implement the
2473+
`DataLayoutTypeInterface`.
2474+
2475+
Examples:
2476+
2477+
```mlir
2478+
// Copying contents from one record to another:
2479+
cir.copy %0 to %1 : !cir.ptr<!record_ty>
2480+
```
2481+
}];
2482+
2483+
let assemblyFormat = [{$src `to` $dst
2484+
attr-dict `:` qualified(type($dst))
2485+
}];
2486+
let hasVerifier = 1;
2487+
2488+
let extraClassDeclaration = [{
2489+
/// Returns the pointer type being copied.
2490+
cir::PointerType getType() { return getSrc().getType(); }
2491+
2492+
/// Returns the number of bytes to be copied.
2493+
unsigned getLength() {
2494+
return mlir::DataLayout::closest(*this).getTypeSize(getType().getPointee());
2495+
}
2496+
}];
2497+
}
2498+
24542499
//===----------------------------------------------------------------------===//
24552500
// ReturnAddrOp and FrameAddrOp
24562501
//===----------------------------------------------------------------------===//

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: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,44 @@ 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+
/*alignment=*/mlir::IntegerAttr{},
145+
/*mem-order=*/cir::MemOrderAttr());
146+
return DeletionKind::Delete;
147+
}
148+
149+
bool cir::CopyOp::canUsesBeRemoved(
150+
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
151+
SmallVectorImpl<OpOperand *> &newBlockingUses,
152+
const DataLayout &dataLayout) {
153+
if (getDst() == getSrc())
154+
return false;
155+
156+
return getLength() == dataLayout.getTypeSize(slot.elemType);
157+
}
158+
121159
//===----------------------------------------------------------------------===//
122160
// Interfaces for CastOp
123161
//===----------------------------------------------------------------------===//

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,17 @@ 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+
const mlir::Value length = mlir::LLVM::ConstantOp::create(
180+
rewriter, op.getLoc(), rewriter.getI32Type(), op.getLength());
181+
assert(!cir::MissingFeatures::aggValueSlotVolatile());
182+
rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
183+
op, adaptor.getDst(), adaptor.getSrc(), length, /*isVolatile=*/false);
184+
return mlir::success();
185+
}
186+
176187
static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter,
177188
mlir::Value llvmSrc, mlir::Type llvmDstIntTy,
178189
bool isUnsigned, uint64_t cirSrcWidth,
@@ -2430,6 +2441,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
24302441
CIRToLLVMComplexRealOpLowering,
24312442
CIRToLLVMComplexRealPtrOpLowering,
24322443
CIRToLLVMComplexSubOpLowering,
2444+
CIRToLLVMCopyOpLowering,
24332445
CIRToLLVMConstantOpLowering,
24342446
CIRToLLVMExpectOpLowering,
24352447
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)