Author: Andy Kaylor Date: 2025-09-03T11:08:40-07:00 New Revision: 8c716bec1dbd00267e2260d558df4d1ab0e5b506
URL: https://github.com/llvm/llvm-project/commit/8c716bec1dbd00267e2260d558df4d1ab0e5b506 DIFF: https://github.com/llvm/llvm-project/commit/8c716bec1dbd00267e2260d558df4d1ab0e5b506.diff LOG: [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. Added: clang/test/CIR/IR/copy.cir clang/test/CIR/IR/invalid-copy.cir Modified: clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h clang/include/clang/CIR/Dialect/IR/CIROps.td clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/CodeGen/CIRGenValue.h clang/lib/CIR/Dialect/IR/CIRDialect.cpp clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h clang/test/CIR/CodeGen/statement-exprs.c clang/test/CIR/CodeGen/variable-decomposition.cpp clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.c Removed: ################################################################################ diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index b2e3841c8d7df..c39518856c3bf 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -245,6 +245,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return createGetGlobal(global.getLoc(), global); } + /// Create a copy with inferred length. + cir::CopyOp createCopy(mlir::Value dst, mlir::Value src) { + return cir::CopyOp::create(*this, dst.getLoc(), dst, src); + } + cir::StoreOp createStore(mlir::Location loc, mlir::Value val, mlir::Value dst, bool isVolatile = false, mlir::IntegerAttr align = {}, diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 10e5d49170f27..4592078af966b 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2451,6 +2451,52 @@ def CIR_CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> { ]; } +//===----------------------------------------------------------------------===// +// CopyOp +//===----------------------------------------------------------------------===// + +def CIR_CopyOp : CIR_Op<"copy",[ + SameTypeOperands, + DeclareOpInterfaceMethods<PromotableMemOpInterface> +]> { + let summary = "Copies contents from a CIR pointer to another"; + let description = [{ + Given two CIR pointers, `src` and `dst`, `cir.copy` will copy the memory + pointed by `src` to the memory pointed by `dst`. + + The number of bytes copied is inferred from the pointee type. The pointee + type of `src` and `dst` must match and both must implement the + `DataLayoutTypeInterface`. + + Examples: + + ```mlir + // Copying contents from one record to another: + cir.copy %0 to %1 : !cir.ptr<!record_ty> + ``` + }]; + + let arguments = (ins + Arg<CIR_PointerType, "", [MemWrite]>:$dst, + Arg<CIR_PointerType, "", [MemRead]>:$src + ); + + let assemblyFormat = [{$src `to` $dst + attr-dict `:` qualified(type($dst)) + }]; + let hasVerifier = 1; + + let extraClassDeclaration = [{ + /// Returns the pointer type being copied. + cir::PointerType getType() { return getSrc().getType(); } + + /// Returns the number of bytes to be copied. + unsigned getLength(const mlir::DataLayout &dt) { + return dt.getTypeSize(getType().getPointee()); + } + }]; +} + //===----------------------------------------------------------------------===// // ReturnAddrOp and FrameAddrOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 70e0abe30e416..7a0ac6a8b5a9d 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -168,6 +168,7 @@ struct MissingFeatures { // Misc static bool abiArgInfo() { return false; } static bool addHeapAllocSiteMetadata() { return false; } + static bool aggEmitFinalDestCopyRValue() { return false; } static bool aggValueSlot() { return false; } static bool aggValueSlotAlias() { return false; } static bool aggValueSlotDestructedFlag() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 05320a0fd576d..2e7fb9843b135 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -62,12 +62,19 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { /// Perform the final copy to DestPtr, if desired. void emitFinalDestCopy(QualType type, const LValue &src); + void emitCopy(QualType type, const AggValueSlot &dest, + const AggValueSlot &src); + void emitInitializationToLValue(Expr *e, LValue lv); void emitNullInitializationToLValue(mlir::Location loc, LValue lv); void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); } + void VisitArraySubscriptExpr(ArraySubscriptExpr *e) { + emitAggLoadOfLValue(e); + } + void VisitCallExpr(const CallExpr *e); void VisitStmtExpr(const StmtExpr *e) { CIRGenFunction::StmtExprEvaluation eval(cgf); @@ -91,13 +98,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { } // Stubs -- These should be moved up when they are implemented. - void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *e) { - // We shouldn't really get here, but we do because of missing handling for - // emitting constant aggregate initializers. If we just ignore this, a - // fallback handler will do the right thing. - assert(!cir::MissingFeatures::constEmitterAggILE()); - return; - } void VisitCastExpr(CastExpr *e) { switch (e->getCastKind()) { case CK_LValueToRValue: @@ -167,10 +167,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCompoundLiteralExpr"); } - void VisitArraySubscriptExpr(ArraySubscriptExpr *e) { - cgf.cgm.errorNYI(e->getSourceRange(), - "AggExprEmitter: VisitArraySubscriptExpr"); - } void VisitPredefinedExpr(const PredefinedExpr *e) { cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitPredefinedExpr"); @@ -460,7 +456,31 @@ void AggExprEmitter::emitFinalDestCopy(QualType type, const LValue &src) { if (dest.isIgnored()) return; - cgf.cgm.errorNYI("emitFinalDestCopy: non-ignored dest is NYI"); + assert(!cir::MissingFeatures::aggValueSlotVolatile()); + assert(!cir::MissingFeatures::aggEmitFinalDestCopyRValue()); + assert(!cir::MissingFeatures::aggValueSlotGC()); + + AggValueSlot srcAgg = AggValueSlot::forLValue(src, AggValueSlot::IsDestructed, + AggValueSlot::IsAliased, + AggValueSlot::MayOverlap); + emitCopy(type, dest, srcAgg); +} + +/// Perform a copy from the source into the destination. +/// +/// \param type - the type of the aggregate being copied; qualifiers are +/// ignored +void AggExprEmitter::emitCopy(QualType type, const AggValueSlot &dest, + const AggValueSlot &src) { + assert(!cir::MissingFeatures::aggValueSlotGC()); + + // If the result of the assignment is used, copy the LHS there also. + // It's volatile if either side is. Use the minimum alignment of + // the two sides. + LValue destLV = cgf.makeAddrLValue(dest.getAddress(), type); + LValue srcLV = cgf.makeAddrLValue(src.getAddress(), type); + assert(!cir::MissingFeatures::aggValueSlotVolatile()); + cgf.emitAggregateCopy(destLV, srcLV, type, dest.mayOverlap()); } void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) { @@ -712,6 +732,68 @@ void CIRGenFunction::emitAggExpr(const Expr *e, AggValueSlot slot) { AggExprEmitter(*this, slot).Visit(const_cast<Expr *>(e)); } +void CIRGenFunction::emitAggregateCopy(LValue dest, LValue src, QualType ty, + AggValueSlot::Overlap_t mayOverlap) { + // TODO(cir): this function needs improvements, commented code for now since + // this will be touched again soon. + assert(!ty->isAnyComplexType() && "Unexpected copy of complex"); + + Address destPtr = dest.getAddress(); + Address srcPtr = src.getAddress(); + + if (getLangOpts().CPlusPlus) { + if (auto *record = ty->getAsCXXRecordDecl()) { + assert((record->hasTrivialCopyConstructor() || + record->hasTrivialCopyAssignment() || + record->hasTrivialMoveConstructor() || + record->hasTrivialMoveAssignment() || + record->hasAttr<TrivialABIAttr>() || record->isUnion()) && + "Trying to aggregate-copy a type without a trivial copy/move " + "constructor or assignment operator"); + // Ignore empty classes in C++. + if (record->isEmpty()) + return; + } + } + + assert(!cir::MissingFeatures::cudaSupport()); + + // Aggregate assignment turns into llvm.memcpy. This is almost valid per + // C99 6.5.16.1p3, which states "If the value being stored in an object is + // read from another object that overlaps in anyway the storage of the first + // object, then the overlap shall be exact and the two objects shall have + // qualified or unqualified versions of a compatible type." + // + // memcpy is not defined if the source and destination pointers are exactly + // equal, but other compilers do this optimization, and almost every memcpy + // implementation handles this case safely. If there is a libc that does not + // safely handle this, we can add a target hook. + + // Get data size info for this aggregate. Don't copy the tail padding if this + // might be a potentially-overlapping subobject, since the tail padding might + // be occupied by a diff erent object. Otherwise, copying it is fine. + TypeInfoChars typeInfo; + if (mayOverlap) + typeInfo = getContext().getTypeInfoDataSizeInChars(ty); + else + typeInfo = getContext().getTypeInfoInChars(ty); + + assert(!cir::MissingFeatures::aggValueSlotVolatile()); + + // NOTE(cir): original codegen would normally convert destPtr and srcPtr to + // i8* since memcpy operates on bytes. We don't need that in CIR because + // cir.copy will operate on any CIR pointer that points to a sized type. + + // Don't do any of the memmove_collectable tests if GC isn't set. + if (cgm.getLangOpts().getGC() != LangOptions::NonGC) + cgm.errorNYI("emitAggregateCopy: GC"); + + [[maybe_unused]] cir::CopyOp copyOp = + builder.createCopy(destPtr.getPointer(), srcPtr.getPointer()); + + assert(!cir::MissingFeatures::opTBAA()); +} + LValue CIRGenFunction::emitAggExprToLValue(const Expr *e) { assert(hasAggregateEvaluationKind(e->getType()) && "Invalid argument!"); Address temp = createMemTemp(e->getType(), getLoc(e->getSourceRange())); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index a547cd36cb35f..7aff189115f13 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -965,6 +965,16 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitAggExprToLValue(const Expr *e); + /// Emit an aggregate copy. + /// + /// \param isVolatile \c true iff either the source or the destination is + /// volatile. + /// \param MayOverlap Whether the tail padding of the destination might be + /// occupied by some other object. More efficient code can often be + /// generated if not. + void emitAggregateCopy(LValue dest, LValue src, QualType eltTy, + AggValueSlot::Overlap_t mayOverlap); + /// Emit code to compute the specified expression which can have any type. The /// result is returned as an RValue struct. If this is an aggregate /// expression, the aggloc/agglocvolatile arguments indicate where the result diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index ac7e1cc1a1db6..ea8625a0fbee5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -379,6 +379,8 @@ class AggValueSlot { mlir::Value getPointer() const { return addr.getPointer(); } + Overlap_t mayOverlap() const { return Overlap_t(overlapFlag); } + IsZeroed_t isZeroed() const { return IsZeroed_t(zeroedFlag); } RValue asRValue() const { diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 80ca2d371792a..bccc0da588a0d 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -1919,6 +1919,21 @@ OpFoldResult cir::UnaryOp::fold(FoldAdaptor adaptor) { return {}; } +//===----------------------------------------------------------------------===// +// CopyOp Definitions +//===----------------------------------------------------------------------===// + +LogicalResult cir::CopyOp::verify() { + // A data layout is required for us to know the number of bytes to be copied. + if (!getType().getPointee().hasTrait<DataLayoutTypeInterface::Trait>()) + return emitError() << "missing data layout for pointee type"; + + if (getSrc() == getDst()) + return emitError() << "source and destination are the same"; + + return mlir::success(); +} + //===----------------------------------------------------------------------===// // GetMemberOp Definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp index 2550c369a9277..7e96ae99b690b 100644 --- a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp @@ -118,6 +118,45 @@ DeletionKind cir::StoreOp::removeBlockingUses( return DeletionKind::Delete; } +//===----------------------------------------------------------------------===// +// Interfaces for CopyOp +//===----------------------------------------------------------------------===// + +bool cir::CopyOp::loadsFrom(const MemorySlot &slot) { + return getSrc() == slot.ptr; +} + +bool cir::CopyOp::storesTo(const MemorySlot &slot) { + return getDst() == slot.ptr; +} + +Value cir::CopyOp::getStored(const MemorySlot &slot, OpBuilder &builder, + Value reachingDef, const DataLayout &dataLayout) { + return cir::LoadOp::create(builder, getLoc(), slot.elemType, getSrc()); +} + +DeletionKind cir::CopyOp::removeBlockingUses( + const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses, + OpBuilder &builder, mlir::Value reachingDefinition, + const DataLayout &dataLayout) { + if (loadsFrom(slot)) + cir::StoreOp::create(builder, getLoc(), reachingDefinition, getDst(), + /*isVolatile=*/false, + /*alignment=*/mlir::IntegerAttr{}, + /*mem-order=*/cir::MemOrderAttr()); + return DeletionKind::Delete; +} + +bool cir::CopyOp::canUsesBeRemoved( + const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses, + SmallVectorImpl<OpOperand *> &newBlockingUses, + const DataLayout &dataLayout) { + if (getDst() == getSrc()) + return false; + + return getLength(dataLayout) == dataLayout.getTypeSize(slot.elemType); +} + //===----------------------------------------------------------------------===// // Interfaces for CastOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 33f4a681a9fe5..3955cf7a9348e 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -173,6 +173,18 @@ mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) { llvm_unreachable("Unknown CIR linkage type"); } +mlir::LogicalResult CIRToLLVMCopyOpLowering::matchAndRewrite( + cir::CopyOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>()); + const mlir::Value length = mlir::LLVM::ConstantOp::create( + rewriter, op.getLoc(), rewriter.getI32Type(), op.getLength(layout)); + assert(!cir::MissingFeatures::aggValueSlotVolatile()); + rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>( + op, adaptor.getDst(), adaptor.getSrc(), length, /*isVolatile=*/false); + return mlir::success(); +} + static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter, mlir::Value llvmSrc, mlir::Type llvmDstIntTy, bool isUnsigned, uint64_t cirSrcWidth, @@ -2430,6 +2442,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { CIRToLLVMComplexRealOpLowering, CIRToLLVMComplexRealPtrOpLowering, CIRToLLVMComplexSubOpLowering, + CIRToLLVMCopyOpLowering, CIRToLLVMConstantOpLowering, CIRToLLVMExpectOpLowering, CIRToLLVMFAbsOpLowering, diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index 656a776035885..2c2aede09b0b2 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -170,6 +170,15 @@ class CIRToLLVMCastOpLowering : public mlir::OpConversionPattern<cir::CastOp> { mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMCopyOpLowering : public mlir::OpConversionPattern<cir::CopyOp> { +public: + using mlir::OpConversionPattern<cir::CopyOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::CopyOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + class CIRToLLVMExpectOpLowering : public mlir::OpConversionPattern<cir::ExpectOp> { public: diff --git a/clang/test/CIR/CodeGen/statement-exprs.c b/clang/test/CIR/CodeGen/statement-exprs.c index 1b54edfe7ec30..f6ec9ecd1b67e 100644 --- a/clang/test/CIR/CodeGen/statement-exprs.c +++ b/clang/test/CIR/CodeGen/statement-exprs.c @@ -5,9 +5,6 @@ // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll // RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG -// This fails because of a non-ignored copy of an aggregate in test3. -// XFAIL: * - int f19(void) { return ({ 3;;4;; }); } @@ -229,6 +226,7 @@ int test3() { return ({ struct S s = {1}; s; }).x; } // CIR: %[[GEP_X_S:.+]] = cir.get_member %[[S]][0] {name = "x"} : !cir.ptr<!rec_S> -> !cir.ptr<!s32i> // CIR: %[[C1:.+]] = cir.const #cir.int<1> : !s32i // CIR: cir.store {{.*}} %[[C1]], %[[GEP_X_S]] : !s32i, !cir.ptr<!s32i> +// CIR: cir.copy %[[S]] to %[[REF_TMP0]] : !cir.ptr<!rec_S> // CIR: } // CIR: %[[GEP_X_TMP:.+]] = cir.get_member %[[REF_TMP0]][0] {name = "x"} : !cir.ptr<!rec_S> -> !cir.ptr<!s32i> // CIR: %[[XVAL:.+]] = cir.load {{.*}} %[[GEP_X_TMP]] : !cir.ptr<!s32i>, !s32i @@ -249,6 +247,7 @@ int test3() { return ({ struct S s = {1}; s; }).x; } // LLVM: [[LBL6]]: // LLVM: %[[GEP_S:.+]] = getelementptr %struct.S, ptr %[[VAR3]], i32 0, i32 0 // LLVM: store i32 1, ptr %[[GEP_S]] +// LLVM: call void @llvm.memcpy.p0.p0.i32(ptr %[[VAR1]], ptr %[[VAR3]], i32 4, i1 false) // LLVM: br label %[[LBL8:.+]] // LLVM: [[LBL8]]: // LLVM: %[[GEP_VAR1:.+]] = getelementptr %struct.S, ptr %[[VAR1]], i32 0, i32 0 diff --git a/clang/test/CIR/CodeGen/variable-decomposition.cpp b/clang/test/CIR/CodeGen/variable-decomposition.cpp index 022d06a97e369..40dfe73c411c9 100644 --- a/clang/test/CIR/CodeGen/variable-decomposition.cpp +++ b/clang/test/CIR/CodeGen/variable-decomposition.cpp @@ -18,7 +18,13 @@ float function() { // CIR-LABEL: cir.func dso_local @_Z8functionv() -> !cir.float // CIR: %[[RETVAL:.+]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["__retval"] -// CIR: %[[STRUCT:.+]] = cir.alloca !rec_some_struct, !cir.ptr<!rec_some_struct>, [""] +// CIR: %[[STRUCT:.+]] = cir.alloca !rec_some_struct, !cir.ptr<!rec_some_struct>, ["", init] +// CIR: %[[MEMBER_A:.+]] = cir.get_member %[[STRUCT]][0] {name = "a"} : !cir.ptr<!rec_some_struct> -> !cir.ptr<!s32i> +// CIR: %[[CONST_1:.+]] = cir.const #cir.int<1> : !s32i +// CIR: cir.store{{.*}} %[[CONST_1]], %[[MEMBER_A]] +// CIR: %[[MEMBER_B:.+]] = cir.get_member %[[STRUCT]][1] {name = "b"} : !cir.ptr<!rec_some_struct> -> !cir.ptr<!cir.float> +// CIR: %[[TWO_FP:.+]] = cir.const #cir.fp<2.000000e+00> : !cir.float +// CIR: cir.store{{.*}} %[[TWO_FP]], %[[MEMBER_B]] // CIR: %[[MEMBER_A:.+]] = cir.get_member %[[STRUCT]][0] {name = "a"} : !cir.ptr<!rec_some_struct> -> !cir.ptr<!s32i> // CIR: %[[LOAD_A:.+]] = cir.load align(4) %[[MEMBER_A]] : !cir.ptr<!s32i>, !s32i // CIR: %[[CAST_A:.+]] = cir.cast(int_to_float, %[[LOAD_A]] : !s32i), !cir.float @@ -33,6 +39,10 @@ float function() { // LLVM: %[[RETVAL:.+]] = alloca float, i64 1 // LLVM: %[[STRUCT:.+]] = alloca %struct.some_struct, i64 1 // LLVM: %[[GEP_A:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 0 +// LLVM: store i32 1, ptr %[[GEP_A]] +// LLVM: %[[GEP_B:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 1 +// LLVM: store float 2.000000e+00, ptr %[[GEP_B]] +// LLVM: %[[GEP_A:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 0 // LLVM: %[[LOAD_A:.+]] = load i32, ptr %[[GEP_A]] // LLVM: %[[CAST_A:.+]] = sitofp i32 %[[LOAD_A]] to float // LLVM: %[[GEP_B:.+]] = getelementptr %struct.some_struct, ptr %[[STRUCT]], i32 0, i32 1 diff --git a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.c b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.c index 7f1480f212d73..ec4f330dade6a 100644 --- a/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.c +++ b/clang/test/CIR/CodeGenOpenACC/compute-firstprivate-clause.c @@ -1,7 +1,4 @@ -// RUN: not %clang_cc1 -fopenacc -triple x86_64-linux-gnu -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir -triple x86_64-linux-pc %s -o - | FileCheck %s - -// This encounters NYI errors because of a non-ignored copy of an aggregate. -// When that is fixed, the 'not' should be removed from the RUN line above. +// RUN: %clang_cc1 -fopenacc -triple x86_64-linux-gnu -Wno-openacc-self-if-potential-conflict -emit-cir -fclangir -triple x86_64-linux-pc %s -o - | FileCheck %s struct NoCopyConstruct {}; @@ -12,19 +9,38 @@ struct NoCopyConstruct {}; // CHECK-NEXT: } copy { // CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>> {{.*}}): // CHECK-NEXT: %[[DECAY_TO:.*]] = cir.cast(array_to_ptrdecay, %[[ARG_TO]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>>), !cir.ptr<!rec_NoCopyConstruct> -// TODO: OpenACC: cir.copy isn't implemented correctly yet, so this doesn't actually do any initialization. +// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.int<0> +// CHECK-NEXT: %[[DECAY_FROM:.*]] = cir.cast(array_to_ptrdecay, %[[ARG_FROM]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>>), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[FROM_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_FROM]] : !cir.ptr<!rec_NoCopyConstruct>, %[[ZERO]] : !u64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: cir.copy %[[FROM_OFFSET:.*]] to %[[DECAY_TO]] : !cir.ptr<!rec_NoCopyConstruct> // // CHECK-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> // CHECK-NEXT: %[[TO_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_TO]] : !cir.ptr<!rec_NoCopyConstruct>, %[[ONE]] : !s64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[ONE:.*]] = cir.const #cir.int<1> +// CHECK-NEXT: %[[DECAY_FROM:.*]] = cir.cast(array_to_ptrdecay, %[[ARG_FROM]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>>), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[FROM_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_FROM]] : !cir.ptr<!rec_NoCopyConstruct>, %[[ONE]] : !u64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: cir.copy %[[FROM_OFFSET]] to %[[TO_OFFSET]] : !cir.ptr<!rec_NoCopyConstruct> // // CHECK-NEXT: %[[TWO:.*]] = cir.const #cir.int<2> // CHECK-NEXT: %[[TO_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_TO]] : !cir.ptr<!rec_NoCopyConstruct>, %[[TWO]] : !s64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[TWO:.*]] = cir.const #cir.int<2> +// CHECK-NEXT: %[[DECAY_FROM:.*]] = cir.cast(array_to_ptrdecay, %[[ARG_FROM]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>>), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[FROM_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_FROM]] : !cir.ptr<!rec_NoCopyConstruct>, %[[TWO]] : !u64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: cir.copy %[[FROM_OFFSET]] to %[[TO_OFFSET]] : !cir.ptr<!rec_NoCopyConstruct> // // CHECK-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> // CHECK-NEXT: %[[TO_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_TO]] : !cir.ptr<!rec_NoCopyConstruct>, %[[THREE]] : !s64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[THREE:.*]] = cir.const #cir.int<3> +// CHECK-NEXT: %[[DECAY_FROM:.*]] = cir.cast(array_to_ptrdecay, %[[ARG_FROM]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>>), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[FROM_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_FROM]] : !cir.ptr<!rec_NoCopyConstruct>, %[[THREE]] : !u64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: cir.copy %[[FROM_OFFSET]] to %[[TO_OFFSET]] : !cir.ptr<!rec_NoCopyConstruct> // // CHECK-NEXT: %[[FOUR:.*]] = cir.const #cir.int<4> // CHECK-NEXT: %[[TO_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_TO]] : !cir.ptr<!rec_NoCopyConstruct>, %[[FOUR]] : !s64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[FOUR:.*]] = cir.const #cir.int<4> +// CHECK-NEXT: %[[DECAY_FROM:.*]] = cir.cast(array_to_ptrdecay, %[[ARG_FROM]] : !cir.ptr<!cir.array<!rec_NoCopyConstruct x 5>>), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: %[[FROM_OFFSET:.*]] = cir.ptr_stride(%[[DECAY_FROM]] : !cir.ptr<!rec_NoCopyConstruct>, %[[FOUR]] : !u64i), !cir.ptr<!rec_NoCopyConstruct> +// CHECK-NEXT: cir.copy %[[FROM_OFFSET]] to %[[TO_OFFSET]] : !cir.ptr<!rec_NoCopyConstruct> // // CHECK-NEXT: acc.yield // CHECK-NEXT: } @@ -129,7 +145,7 @@ struct NoCopyConstruct {}; // CHECK-NEXT: acc.yield // CHECK-NEXT: } copy { // CHECK-NEXT: ^bb0(%[[ARG_FROM:.*]]: !cir.ptr<!rec_NoCopyConstruct> {{.*}}, %[[ARG_TO:.*]]: !cir.ptr<!rec_NoCopyConstruct> {{.*}}): -// TODO: OpenACC: This should emit a copy, but cir.copy isn't implemented yet. +// CHECK-NEXT: cir.copy %[[ARG_FROM]] to %[[ARG_TO]] : !cir.ptr<!rec_NoCopyConstruct> // CHECK-NEXT: acc.yield // CHECK-NEXT: } // diff --git a/clang/test/CIR/IR/copy.cir b/clang/test/CIR/IR/copy.cir new file mode 100644 index 0000000000000..2cfb25d82b278 --- /dev/null +++ b/clang/test/CIR/IR/copy.cir @@ -0,0 +1,10 @@ +// RUN: cir-opt %s + +!s32i = !cir.int<s, 32> +module { + cir.func @shouldParseCopyOp(%arg0 : !cir.ptr<!s32i>, %arg1 : !cir.ptr<!s32i>) { + cir.copy %arg0 to %arg1 : !cir.ptr<!s32i> + // CHECK: cir.copy %arg0 to %arg1 : !cir.ptr<!s32i> + cir.return + } +} diff --git a/clang/test/CIR/IR/invalid-copy.cir b/clang/test/CIR/IR/invalid-copy.cir new file mode 100644 index 0000000000000..5801b7401acd7 --- /dev/null +++ b/clang/test/CIR/IR/invalid-copy.cir @@ -0,0 +1,21 @@ +// RUN: cir-opt %s -verify-diagnostics -split-input-file + +module { + // Should not copy types with no data layout (unkonwn byte size). + cir.func @invalid_copy(%arg0 : !cir.ptr<!cir.void>, %arg1 : !cir.ptr<!cir.void>) { + // expected-error@+1 {{missing data layout for pointee type}} + cir.copy %arg0 to %arg1 : !cir.ptr<!cir.void> + cir.return + } +} + +// ----- + +module { + // Should not copy to same address. + cir.func @invalid_copy(%arg0 : !cir.ptr<!cir.int<s, 8>>) { + // expected-error@+1 {{source and destination are the same}} + cir.copy %arg0 to %arg0 : !cir.ptr<!cir.int<s, 8>> + cir.return + } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits