[clang] [CIR] Function calls with aggregate arguments and return values (PR #143377)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 10 07:02:36 PDT 2025
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/143377
>From ea7297baf0d801f892065523749903a14aa59ee0 Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at 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 | 5 +
clang/lib/CIR/CodeGen/CIRGenBuilder.h | 12 ++
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 102 ++++++++++++++--
clang/lib/CIR/CodeGen/CIRGenCall.h | 22 +++-
clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 12 +-
clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 82 +++++++++++++
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 | 111 ++++++++++++++++++
clang/test/CIR/CodeGen/call.cpp | 32 +++++
11 files changed, 406 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..4bf028dcb82ae 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; }
@@ -216,6 +220,7 @@ struct MissingFeatures {
static bool peepholeProtection() { return false; }
static bool instrumentation() { return false; }
static bool cleanupAfterErrorDiags() { return false; }
+ static bool lowerAggregateLoadStore() { 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 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..989c664e310f1 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);
+}
+
/// 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,48 @@ 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) {
+ 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.
+ assert(!cir::MissingFeatures::lowerAggregateLoadStore());
+ cirCallArgs[argNo] = builder.createLoad(argLoc, src);
}
}
@@ -352,6 +409,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 +423,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");
@@ -381,7 +452,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 +470,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 +505,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..77aa847c73207 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,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 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..6ec856b2bfe15 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 };
+ /// 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..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
More information about the cfe-commits
mailing list