https://github.com/Lancern created https://github.com/llvm/llvm-project/pull/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 try 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. Thus, tests for calling functions with aggregate arguments are added only for C for now. >From 96fb8413e33309f4c77994d2a512a7d1fdf861f9 Mon Sep 17 00:00:00 2001 From: Sirui Mu <msrlanc...@gmail.com> Date: Mon, 9 Jun 2025 18:35:08 +0800 Subject: [PATCH] [CIR] Function calls with aggregate arguments and return values 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 try 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. Thus, tests for calling functions with aggregate arguments are added only for C for now. --- clang/include/clang/CIR/MissingFeatures.h | 4 + clang/lib/CIR/CodeGen/CIRGenBuilder.h | 12 +++ clang/lib/CIR/CodeGen/CIRGenCall.cpp | 101 ++++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenCall.h | 22 +++- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 12 ++- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 81 ++++++++++++++ clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 11 ++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 18 +++- clang/lib/CIR/CodeGen/CIRGenValue.h | 15 +++ clang/test/CIR/CodeGen/call.c | 23 ++++ clang/test/CIR/CodeGen/call.cpp | 32 ++++++ 11 files changed, 315 insertions(+), 16 deletions(-) create mode 100644 clang/test/CIR/CodeGen/call.c diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index da1946ed73746..5ab651aefa9f0 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -166,6 +166,10 @@ struct MissingFeatures { static bool astVarDeclInterface() { return false; } static bool stackSaveOp() { return false; } static bool aggValueSlot() { 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; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 5f17b5d58acaa..e849a50ec312e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -281,6 +281,18 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { return create<cir::BinOp>(loc, cir::BinOpKind::Div, lhs, rhs); } + /// Cast the element type of the given address to a different 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 b194a8670bfb9..127f1e80452f2 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 LLVM 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); +} + /// Adds the formal parameters in FPT to the given prefix. If any parameter in /// FPT has pass_object_size_attrs, then we'll add parameters for those, too. /// TODO(cir): this should be shared with LLVM codegen @@ -312,8 +329,47 @@ 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); + auto srcTy = src.getElementType(); + // FIXME(cir): get proper location for each argument. + auto 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) { + 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 different, + // but should be when alloc sizes are different, fix this as soon as + // datalayout gets introduced. + src = builder.createElementBitCast(argLoc, src, argRecordTy); + } + + // 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. + cirCallArgs[argNo] = builder.createLoad(argLoc, src); } } @@ -352,6 +408,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); @@ -365,6 +422,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()); + + auto 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"); @@ -381,7 +451,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); } @@ -400,10 +469,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); @@ -424,12 +504,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 605625705a75c..64a343ebc1028 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -105,8 +105,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; @@ -119,6 +127,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. @@ -134,7 +146,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 1175fdc0be2cf..f99cbf4f22557 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -985,16 +985,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 56d7ea3884ba7..5aa476a974528 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 visitCXXParenListOrInitListExpr(Expr *e, ArrayRef<Expr *> args, @@ -79,6 +100,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) { @@ -181,6 +213,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(); @@ -240,6 +284,43 @@ 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()) { + llvm_unreachable("NYI"); + } + + 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 difference, 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 b008ee9b472a1..9eb0b45717102 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -586,6 +586,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 ee014adc961be..b693567e37012 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -309,6 +309,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); @@ -637,6 +641,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); @@ -645,7 +651,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. @@ -1047,6 +1054,15 @@ 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()); + } + private: QualType getVarArgType(const Expr *arg); }; diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index 208247e16e531..3d0440fc55a9b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -274,6 +274,12 @@ class AggValueSlot { public: enum IsZeroed_t { IsNotZeroed, IsZeroed }; + /// ignored - Returns an aggregate value slot indicating that the aggregate + /// value is being ignored. + static AggValueSlot ignored() { + return forAddr(Address::invalid(), clang::Qualifiers()); + } + AggValueSlot(Address addr, clang::Qualifiers quals, bool zeroedFlag) : addr(addr), quals(quals), zeroedFlag(zeroedFlag) {} @@ -292,7 +298,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..4a61b083795e4 --- /dev/null +++ b/clang/test/CIR/CodeGen/call.c @@ -0,0 +1,23 @@ +// 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 + +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]]) 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