Author: Sirui Mu Date: 2025-06-13T16:47:56+08:00 New Revision: 8ba62fdb3d2da2f5f199ee7a07222620a451293f
URL: https://github.com/llvm/llvm-project/commit/8ba62fdb3d2da2f5f199ee7a07222620a451293f DIFF: https://github.com/llvm/llvm-project/commit/8ba62fdb3d2da2f5f199ee7a07222620a451293f.diff LOG: [CIR] Function calls with aggregate arguments and return values (#143377) This patch updates cir.call operation and allows function calls with aggregate arguments and return values. It seems that C++ class support is still at a minimum now. I tried to make a call to a C++ function with an argument of aggregate type but it failed because the initialization of C++ class / struct is NYI. I also tried to inline this part of support into this patch, but the mixed patch quickly blows in size and becomes unsuitable for review. Thus, tests for calling functions with aggregate arguments are added only for C for now. Added: clang/test/CIR/CodeGen/call.c Modified: clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/CIRGenBuilder.h clang/lib/CIR/CodeGen/CIRGenCall.cpp clang/lib/CIR/CodeGen/CIRGenCall.h clang/lib/CIR/CodeGen/CIRGenExpr.cpp clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp clang/lib/CIR/CodeGen/CIRGenFunction.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h clang/lib/CIR/CodeGen/CIRGenValue.h clang/test/CIR/CodeGen/call.cpp Removed: ################################################################################ diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 97b933657d742..225e9ec89a827 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -173,6 +173,10 @@ struct MissingFeatures { static bool stackSaveOp() { return false; } static bool aggValueSlot() { return false; } static bool aggValueSlotMayOverlap() { return false; } + static bool aggValueSlotVolatile() { return false; } + static bool aggValueSlotDestructedFlag() { return false; } + static bool aggValueSlotAlias() { return false; } + static bool aggValueSlotGC() { return false; } static bool generateDebugInfo() { return false; } static bool pointerOverflowSanitizer() { return false; } static bool fpConstraints() { return false; } @@ -230,6 +234,8 @@ struct MissingFeatures { static bool attributeNoBuiltin() { return false; } static bool thunks() { return false; } static bool runCleanupsScope() { return false; } + static bool lowerAggregateLoadStore() { return false; } + static bool dataLayoutTypeAllocSize() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index fb1a290c18fa2..36c89809b4d90 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -332,6 +332,18 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return Address(baseAddr, destType, addr.getAlignment()); } + /// Cast the element type of the given address to a diff erent type, + /// preserving information like the alignment. + Address createElementBitCast(mlir::Location loc, Address addr, + mlir::Type destType) { + if (destType == addr.getElementType()) + return addr; + + auto ptrTy = getPointerTo(destType); + return Address(createBitcast(loc, addr.getPointer(), ptrTy), destType, + addr.getAlignment()); + } + cir::LoadOp createLoad(mlir::Location loc, Address addr, bool isVolatile = false) { mlir::IntegerAttr align = getAlignmentAttr(addr.getAlignment()); diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 5ec720ffd54f1..0d9064425fa95 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -60,6 +60,23 @@ CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const { return *this; } +void CIRGenFunction::emitAggregateStore(mlir::Value value, Address dest) { + // In classic codegen: + // Function to store a first-class aggregate into memory. We prefer to + // store the elements rather than the aggregate to be more friendly to + // fast-isel. + // In CIR codegen: + // Emit the most simple cir.store possible (e.g. a store for a whole + // record), which can later be broken down in other CIR levels (or prior + // to dialect codegen). + + // Stored result for the callers of this function expected to be in the same + // scope as the value, don't make assumptions about current insertion point. + mlir::OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPointAfter(value.getDefiningOp()); + builder.createStore(*currSrcLoc, value, dest); +} + /// Returns the canonical formal type of the given C++ method. static CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *md) { return md->getType() @@ -439,8 +456,49 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, assert(!cir::MissingFeatures::opCallBitcastArg()); cirCallArgs[argNo] = v; } else { - assert(!cir::MissingFeatures::opCallAggregateArgs()); - cgm.errorNYI("emitCall: aggregate function call argument"); + Address src = Address::invalid(); + if (!arg.isAggregate()) + cgm.errorNYI(loc, "emitCall: non-aggregate call argument"); + else + src = arg.hasLValue() ? arg.getKnownLValue().getAddress() + : arg.getKnownRValue().getAggregateAddress(); + + // Fast-isel and the optimizer generally like scalar values better than + // FCAs, so we flatten them if this is safe to do for this argument. + auto argRecordTy = cast<cir::RecordType>(argType); + mlir::Type srcTy = src.getElementType(); + // FIXME(cir): get proper location for each argument. + mlir::Location argLoc = loc; + + // If the source type is smaller than the destination type of the + // coerce-to logic, copy the source value into a temp alloca the size + // of the destination type to allow loading all of it. The bits past + // the source value are left undef. + // FIXME(cir): add data layout info and compare sizes instead of + // matching the types. + // + // uint64_t SrcSize = CGM.getDataLayout().getTypeAllocSize(SrcTy); + // uint64_t DstSize = CGM.getDataLayout().getTypeAllocSize(STy); + // if (SrcSize < DstSize) { + assert(!cir::MissingFeatures::dataLayoutTypeAllocSize()); + if (srcTy != argRecordTy) { + cgm.errorNYI(loc, "emitCall: source type does not match argument type"); + } else { + // FIXME(cir): this currently only runs when the types are exactly the + // same, but should be when alloc sizes are the same, fix this as soon + // as datalayout gets introduced. + assert(!cir::MissingFeatures::dataLayoutTypeAllocSize()); + } + + // assert(NumCIRArgs == STy.getMembers().size()); + // In LLVMGen: Still only pass the struct without any gaps but mark it + // as such somehow. + // + // In CIRGen: Emit a load from the "whole" struct, + // which shall be broken later by some lowering step into multiple + // loads. + assert(!cir::MissingFeatures::lowerAggregateLoadStore()); + cirCallArgs[argNo] = builder.createLoad(argLoc, src); } } @@ -479,6 +537,7 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, assert(!cir::MissingFeatures::opCallAttrs()); + mlir::Location callLoc = loc; cir::CIRCallOpInterface theCall = emitCallLikeOp( *this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, cirCallArgs); @@ -492,6 +551,19 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, if (isa<cir::VoidType>(retCIRTy)) return getUndefRValue(retTy); switch (getEvaluationKind(retTy)) { + case cir::TEK_Aggregate: { + Address destPtr = returnValue.getValue(); + + if (!destPtr.isValid()) + destPtr = createMemTemp(retTy, callLoc, getCounterAggTmpAsString()); + + mlir::ResultRange results = theCall->getOpResults(); + assert(results.size() <= 1 && "multiple returns from a call"); + + SourceLocRAIIObject loc{*this, callLoc}; + emitAggregateStore(results[0], destPtr); + return RValue::getAggregate(destPtr); + } case cir::TEK_Scalar: { mlir::ResultRange results = theCall->getOpResults(); assert(results.size() == 1 && "unexpected number of returns"); @@ -508,7 +580,6 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, return RValue::get(results[0]); } case cir::TEK_Complex: - case cir::TEK_Aggregate: cgm.errorNYI(loc, "unsupported evaluation kind of function call result"); return getUndefRValue(retTy); } @@ -527,10 +598,21 @@ void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e, bool hasAggregateEvalKind = hasAggregateEvaluationKind(argType); - if (hasAggregateEvalKind) { - assert(!cir::MissingFeatures::opCallAggregateArgs()); - cgm.errorNYI(e->getSourceRange(), - "emitCallArg: aggregate function call argument"); + // In the Microsoft C++ ABI, aggregate arguments are destructed by the callee. + // However, we still have to push an EH-only cleanup in case we unwind before + // we make it to the call. + if (argType->isRecordType() && + argType->castAs<RecordType>()->getDecl()->isParamDestroyedInCallee()) { + assert(!cir::MissingFeatures::msabi()); + cgm.errorNYI(e->getSourceRange(), "emitCallArg: msabi is NYI"); + } + + if (hasAggregateEvalKind && isa<ImplicitCastExpr>(e) && + cast<CastExpr>(e)->getCastKind() == CK_LValueToRValue) { + LValue lv = emitLValue(cast<CastExpr>(e)->getSubExpr()); + assert(lv.isSimple()); + args.addUncopiedAggregate(lv, argType); + return; } args.add(emitAnyExprToTemp(e), argType); @@ -551,12 +633,13 @@ QualType CIRGenFunction::getVarArgType(const Expr *arg) { /// Similar to emitAnyExpr(), however, the result will always be accessible /// even if no aggregate location is provided. RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) { - assert(!cir::MissingFeatures::opCallAggregateArgs()); + AggValueSlot aggSlot = AggValueSlot::ignored(); if (hasAggregateEvaluationKind(e->getType())) - cgm.errorNYI(e->getSourceRange(), "emit aggregate value to temp"); + aggSlot = createAggTemp(e->getType(), getLoc(e->getSourceRange()), + getCounterAggTmpAsString()); - return emitAnyExpr(e); + return emitAnyExpr(e, aggSlot); } void CIRGenFunction::emitCallArgs( diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index 15c9080448c8b..0353848f3ec0d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -133,8 +133,16 @@ struct CallArg { CallArg(RValue rv, clang::QualType ty) : rv(rv), hasLV(false), isUsed(false), ty(ty) {} + CallArg(LValue lv, clang::QualType ty) + : lv(lv), hasLV(true), isUsed(false), ty(ty) {} + bool hasLValue() const { return hasLV; } + LValue getKnownLValue() const { + assert(hasLV && !isUsed); + return lv; + } + RValue getKnownRValue() const { assert(!hasLV && !isUsed); return rv; @@ -147,6 +155,10 @@ class CallArgList : public llvm::SmallVector<CallArg, 8> { public: void add(RValue rvalue, clang::QualType type) { emplace_back(rvalue, type); } + void addUncopiedAggregate(LValue lvalue, clang::QualType type) { + emplace_back(lvalue, type); + } + /// Add all the arguments from another CallArgList to this one. After doing /// this, the old CallArgList retains its list of arguments, but must not /// be used to emit a call. @@ -162,7 +174,15 @@ class CallArgList : public llvm::SmallVector<CallArg, 8> { /// Contains the address where the return value of a function can be stored, and /// whether the address is volatile or not. -class ReturnValueSlot {}; +class ReturnValueSlot { + Address addr = Address::invalid(); + +public: + ReturnValueSlot() = default; + ReturnValueSlot(Address addr) : addr(addr) {} + + Address getValue() const { return addr; } +}; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 5d04faf443b8d..99f942fcf2cd3 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1010,16 +1010,20 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) { /// Emit code to compute the specified expression which /// can have any type. The result is returned as an RValue struct. -RValue CIRGenFunction::emitAnyExpr(const Expr *e) { +RValue CIRGenFunction::emitAnyExpr(const Expr *e, AggValueSlot aggSlot) { switch (CIRGenFunction::getEvaluationKind(e->getType())) { case cir::TEK_Scalar: return RValue::get(emitScalarExpr(e)); case cir::TEK_Complex: cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: complex type"); return RValue::get(nullptr); - case cir::TEK_Aggregate: - cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: aggregate type"); - return RValue::get(nullptr); + case cir::TEK_Aggregate: { + if (aggSlot.isIgnored()) + aggSlot = createAggTemp(e->getType(), getLoc(e->getSourceRange()), + getCounterAggTmpAsString()); + emitAggExpr(e, aggSlot); + return aggSlot.asRValue(); + } } llvm_unreachable("bad evaluation kind"); } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 061123d55b882..ffe1b701b244e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -28,6 +28,15 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { CIRGenFunction &cgf; AggValueSlot dest; + // Calls `fn` with a valid return value slot, potentially creating a temporary + // to do so. If a temporary is created, an appropriate copy into `Dest` will + // be emitted, as will lifetime markers. + // + // The given function should take a ReturnValueSlot, and return an RValue that + // points to said slot. + void withReturnValueSlot(const Expr *e, + llvm::function_ref<RValue(ReturnValueSlot)> fn); + AggValueSlot ensureSlot(mlir::Location loc, QualType t) { if (!dest.isIgnored()) return dest; @@ -40,16 +49,28 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest) : cgf(cgf), dest(dest) {} + /// Given an expression with aggregate type that represents a value lvalue, + /// this method emits the address of the lvalue, then loads the result into + /// DestPtr. + void emitAggLoadOfLValue(const Expr *e); + void emitArrayInit(Address destPtr, cir::ArrayType arrayTy, QualType arrayQTy, Expr *exprToVisit, ArrayRef<Expr *> args, Expr *arrayFiller); + /// Perform the final copy to DestPtr, if desired. + void emitFinalDestCopy(QualType type, const LValue &src); + void emitInitializationToLValue(Expr *e, LValue lv); void emitNullInitializationToLValue(mlir::Location loc, LValue lv); void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); } + void VisitCallExpr(const CallExpr *e); + + void VisitDeclRefExpr(DeclRefExpr *e) { emitAggLoadOfLValue(e); } + void VisitInitListExpr(InitListExpr *e); void VisitCXXConstructExpr(const CXXConstructExpr *e); @@ -80,6 +101,17 @@ static bool isTrivialFiller(Expr *e) { return false; } +/// Given an expression with aggregate type that represents a value lvalue, this +/// method emits the address of the lvalue, then loads the result into DestPtr. +void AggExprEmitter::emitAggLoadOfLValue(const Expr *e) { + LValue lv = cgf.emitLValue(e); + + // If the type of the l-value is atomic, then do an atomic load. + assert(!cir::MissingFeatures::opLoadStoreAtomic()); + + emitFinalDestCopy(e->getType(), lv); +} + void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy, QualType arrayQTy, Expr *e, ArrayRef<Expr *> args, Expr *arrayFiller) { @@ -182,6 +214,18 @@ void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy, } } +/// Perform the final copy to destPtr, if desired. +void AggExprEmitter::emitFinalDestCopy(QualType type, const LValue &src) { + // If dest is ignored, then we're evaluating an aggregate expression + // in a context that doesn't care about the result. Note that loads + // from volatile l-values force the existence of a non-ignored + // destination. + if (dest.isIgnored()) + return; + + cgf.cgm.errorNYI("emitFinalDestCopy: non-ignored dest is NYI"); +} + void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) { const QualType type = lv.getType(); @@ -250,6 +294,44 @@ void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc, cgf.emitNullInitialization(loc, lv.getAddress(), lv.getType()); } +void AggExprEmitter::VisitCallExpr(const CallExpr *e) { + if (e->getCallReturnType(cgf.getContext())->isReferenceType()) { + cgf.cgm.errorNYI(e->getSourceRange(), "reference return type"); + return; + } + + withReturnValueSlot( + e, [&](ReturnValueSlot slot) { return cgf.emitCallExpr(e, slot); }); +} + +void AggExprEmitter::withReturnValueSlot( + const Expr *e, llvm::function_ref<RValue(ReturnValueSlot)> fn) { + QualType retTy = e->getType(); + + assert(!cir::MissingFeatures::aggValueSlotDestructedFlag()); + bool requiresDestruction = + retTy.isDestructedType() == QualType::DK_nontrivial_c_struct; + if (requiresDestruction) + cgf.cgm.errorNYI( + e->getSourceRange(), + "withReturnValueSlot: return value requiring destruction is NYI"); + + // If it makes no observable diff erence, save a memcpy + temporary. + // + // We need to always provide our own temporary if destruction is required. + // Otherwise, fn will emit its own, notice that it's "unused", and end its + // lifetime before we have the chance to emit a proper destructor call. + assert(!cir::MissingFeatures::aggValueSlotAlias()); + assert(!cir::MissingFeatures::aggValueSlotGC()); + + Address retAddr = dest.getAddress(); + assert(!cir::MissingFeatures::emitLifetimeMarkers()); + + assert(!cir::MissingFeatures::aggValueSlotVolatile()); + assert(!cir::MissingFeatures::aggValueSlotDestructedFlag()); + fn(ReturnValueSlot(retAddr)); +} + void AggExprEmitter::VisitInitListExpr(InitListExpr *e) { if (e->hadArrayRangeDesignator()) llvm_unreachable("GNU array range designator extension"); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index c5bd5109343d3..fd413fe86383a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -629,6 +629,17 @@ LValue CIRGenFunction::emitLValue(const Expr *e) { } } +static std::string getVersionedTmpName(llvm::StringRef name, unsigned cnt) { + SmallString<256> buffer; + llvm::raw_svector_ostream out(buffer); + out << name << cnt; + return std::string(out.str()); +} + +std::string CIRGenFunction::getCounterAggTmpAsString() { + return getVersionedTmpName("agg.tmp", counterAggTmp++); +} + void CIRGenFunction::emitNullInitialization(mlir::Location loc, Address destPtr, QualType ty) { // Ignore empty classes in C++. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index cf672b0c90e60..9421ea26a429f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -316,6 +316,10 @@ class CIRGenFunction : public CIRGenTypeCache { ~SourceLocRAIIObject() { restore(); } }; + /// Hold counters for incrementally naming temporaries + unsigned counterAggTmp = 0; + std::string getCounterAggTmpAsString(); + /// Helpers to convert Clang's SourceLocation to a MLIR Location. mlir::Location getLoc(clang::SourceLocation srcLoc); mlir::Location getLoc(clang::SourceRange srcLoc); @@ -695,6 +699,8 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::OpBuilder::InsertPoint ip, mlir::Value arraySize = nullptr); + void emitAggregateStore(mlir::Value value, Address dest); + void emitAggExpr(const clang::Expr *e, AggValueSlot slot); LValue emitAggExprToLValue(const Expr *e); @@ -703,7 +709,8 @@ class CIRGenFunction : public CIRGenTypeCache { /// result is returned as an RValue struct. If this is an aggregate /// expression, the aggloc/agglocvolatile arguments indicate where the result /// should be returned. - RValue emitAnyExpr(const clang::Expr *e); + RValue emitAnyExpr(const clang::Expr *e, + AggValueSlot aggSlot = AggValueSlot::ignored()); /// Similarly to emitAnyExpr(), however, the result will always be accessible /// even if no aggregate location is provided. @@ -1152,6 +1159,17 @@ class CIRGenFunction : public CIRGenTypeCache { void emitOpenACCDeclare(const OpenACCDeclareDecl &d); void emitOpenACCRoutine(const OpenACCRoutineDecl &d); + /// Create a temporary memory object for the given aggregate type. + AggValueSlot createAggTemp(QualType ty, mlir::Location loc, + const Twine &name = "tmp", + Address *alloca = nullptr) { + assert(!cir::MissingFeatures::aggValueSlot()); + return AggValueSlot::forAddr( + createMemTemp(ty, loc, name, alloca), ty.getQualifiers(), + AggValueSlot::IsNotDestructed, AggValueSlot::IsNotAliased, + AggValueSlot::DoesNotOverlap); + } + private: QualType getVarArgType(const Expr *arg); }; diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index 258ae306f693d..c1e08ba1e9b67 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -306,6 +306,13 @@ class AggValueSlot { enum IsAliased_t { IsNotAliased, IsAliased }; enum Overlap_t { MayOverlap, DoesNotOverlap }; + /// Returns an aggregate value slot indicating that the aggregate + /// value is being ignored. + static AggValueSlot ignored() { + return forAddr(Address::invalid(), clang::Qualifiers(), IsNotDestructed, + IsNotAliased, DoesNotOverlap); + } + AggValueSlot(Address addr, clang::Qualifiers quals, bool destructedFlag, bool zeroedFlag, bool aliasedFlag, bool overlapFlag) : addr(addr), quals(quals), destructedFlag(destructedFlag), @@ -333,7 +340,16 @@ class AggValueSlot { bool isIgnored() const { return !addr.isValid(); } + mlir::Value getPointer() const { return addr.getPointer(); } + IsZeroed_t isZeroed() const { return IsZeroed_t(zeroedFlag); } + + RValue asRValue() const { + if (isIgnored()) + return RValue::getIgnored(); + assert(!cir::MissingFeatures::aggValueSlot()); + return RValue::getAggregate(getAddress()); + } }; } // namespace clang::CIRGen diff --git a/clang/test/CIR/CodeGen/call.c b/clang/test/CIR/CodeGen/call.c new file mode 100644 index 0000000000000..13f3c5a21ceb0 --- /dev/null +++ b/clang/test/CIR/CodeGen/call.c @@ -0,0 +1,111 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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 -Wno-unused-value -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 -Wno-unused-value -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +struct S { + int x; + int y; +}; + +void f1(struct S); +void f2() { + struct S s; + f1(s); +} + +// CIR-LABEL: cir.func @f2() +// CIR: %[[S:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!rec_S>, !rec_S +// CIR-NEXT: cir.call @f1(%[[S]]) : (!rec_S) -> () + +// LLVM-LABEL: define void @f2() +// LLVM: %[[S:.+]] = load %struct.S, ptr %{{.+}}, align 4 +// LLVM-NEXT: call void @f1(%struct.S %[[S]]) + +// OGCG-LABEL: define dso_local void @f2() +// OGCG: %[[S:.+]] = load i64, ptr %{{.+}}, align 4 +// OGCG-NEXT: call void @f1(i64 %[[S]]) + +struct S f3(); +void f4() { + struct S s = f3(); +} + +// CIR-LABEL: cir.func @f4() { +// CIR: %[[S:.+]] = cir.call @f3() : () -> !rec_S +// CIR-NEXT: cir.store align(4) %[[S]], %{{.+}} : !rec_S, !cir.ptr<!rec_S> + +// LLVM-LABEL: define void @f4() { +// LLVM: %[[S:.+]] = call %struct.S (...) @f3() +// LLVM-NEXT: store %struct.S %[[S]], ptr %{{.+}}, align 4 + +// OGCG-LABEL: define dso_local void @f4() #0 { +// OGCG: %[[S:.+]] = call i64 (...) @f3() +// OGCG-NEXT: store i64 %[[S]], ptr %{{.+}}, align 4 + +struct Big { + int data[10]; +}; + +void f5(struct Big); +struct Big f6(); + +void f7() { + struct Big b; + f5(b); +} + +// CIR-LABEL: cir.func @f7() +// CIR: %[[B:.+]] = cir.load align(4) %{{.+}} : !cir.ptr<!rec_Big>, !rec_Big +// CIR-NEXT: cir.call @f5(%[[B]]) : (!rec_Big) -> () + +// LLVM-LABEL: define void @f7() { +// LLVM: %[[B:.+]] = load %struct.Big, ptr %{{.+}}, align 4 +// LLVM-NEXT: call void @f5(%struct.Big %[[B]]) + +// OGCG-LABEL: define dso_local void @f7() #0 { +// OGCG: %[[B:.+]] = alloca %struct.Big, align 8 +// OGCG-NEXT: call void @f5(ptr noundef byval(%struct.Big) align 8 %[[B]]) + +void f8() { + struct Big b = f6(); +} + +// CIR-LABEL: cir.func @f8() +// CIR: %[[B:.+]] = cir.call @f6() : () -> !rec_Big +// CIR: cir.store align(4) %[[B]], %{{.+}} : !rec_Big, !cir.ptr<!rec_Big> + +// LLVM-LABEL: define void @f8() { +// LLVM: %[[B:.+]] = call %struct.Big (...) @f6() +// LLVM-NEXT: store %struct.Big %[[B]], ptr %{{.+}}, align 4 + +// OGCG-LABEL: define dso_local void @f8() #0 { +// OGCG: %[[B:.+]] = alloca %struct.Big, align 4 +// OGCG-NEXT: call void (ptr, ...) @f6(ptr dead_on_unwind writable sret(%struct.Big) align 4 %[[B]]) + +void f9() { + f1(f3()); +} + +// CIR-LABEL: cir.func @f9() +// CIR: %[[SLOT:.+]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["agg.tmp0"] {alignment = 4 : i64} +// CIR-NEXT: %[[RET:.+]] = cir.call @f3() : () -> !rec_S +// CIR-NEXT: cir.store align(4) %[[RET]], %[[SLOT]] : !rec_S, !cir.ptr<!rec_S> +// CIR-NEXT: %[[ARG:.+]] = cir.load align(4) %[[SLOT]] : !cir.ptr<!rec_S>, !rec_S +// CIR-NEXT: cir.call @f1(%[[ARG]]) : (!rec_S) -> () + +// LLVM-LABEL: define void @f9() { +// LLVM: %[[SLOT:.+]] = alloca %struct.S, i64 1, align 4 +// LLVM-NEXT: %[[RET:.+]] = call %struct.S (...) @f3() +// LLVM-NEXT: store %struct.S %[[RET]], ptr %[[SLOT]], align 4 +// LLVM-NEXT: %[[ARG:.+]] = load %struct.S, ptr %[[SLOT]], align 4 +// LLVM-NEXT: call void @f1(%struct.S %[[ARG]]) + +// OGCG-LABEL: define dso_local void @f9() #0 { +// OGCG: %[[SLOT:.+]] = alloca %struct.S, align 4 +// OGCG-NEXT: %[[RET:.+]] = call i64 (...) @f3() +// OGCG-NEXT: store i64 %[[RET]], ptr %[[SLOT]], align 4 +// OGCG-NEXT: %[[ARG:.+]] = load i64, ptr %[[SLOT]], align 4 +// OGCG-NEXT: call void @f1(i64 %[[ARG]]) diff --git a/clang/test/CIR/CodeGen/call.cpp b/clang/test/CIR/CodeGen/call.cpp index 741cadeb5c764..cc25afce1e5a4 100644 --- a/clang/test/CIR/CodeGen/call.cpp +++ b/clang/test/CIR/CodeGen/call.cpp @@ -70,3 +70,35 @@ void f9() { // LLVM-LABEL: define void @_Z2f9v() // LLVM: call void (i32, ...) @_Z2f8iz(i32 1) // LLVM: call void (i32, ...) @_Z2f8iz(i32 1, i32 2, i32 3, i32 4) + +struct S { + int x; + int y; +}; + +S f10(); +void f11() { + S s = f10(); +} + +// CIR-LABEL: cir.func @_Z3f11v() +// CIR: %[[#s:]] = cir.call @_Z3f10v() : () -> !rec_S +// CIR-NEXT: cir.store align(4) %[[#s]], %{{.+}} : !rec_S, !cir.ptr<!rec_S> + +// LLVM-LABEL: define void @_Z3f11v() +// LLVM: %[[#s:]] = call %struct.S @_Z3f10v() +// LLVM-NEXT: store %struct.S %[[#s]], ptr %{{.+}}, align 4 + +void f12() { + f10(); +} + +// CIR-LABEL: cir.func @_Z3f12v() +// CIR: %[[#slot:]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["agg.tmp0"] +// CIR-NEXT: %[[#ret:]] = cir.call @_Z3f10v() : () -> !rec_S +// CIR-NEXT: cir.store align(4) %[[#ret]], %[[#slot]] : !rec_S, !cir.ptr<!rec_S> + +// LLVM-LABEL: define void @_Z3f12v() { +// LLVM: %[[#slot:]] = alloca %struct.S, i64 1, align 4 +// LLVM-NEXT: %[[#ret:]] = call %struct.S @_Z3f10v() +// LLVM-NEXT: store %struct.S %[[#ret]], ptr %[[#slot]], align 4 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits