Skip to content

Commit df020df

Browse files
committed
[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.
1 parent d75e284 commit df020df

File tree

11 files changed

+406
-16
lines changed

11 files changed

+406
-16
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,10 @@ struct MissingFeatures {
172172
static bool astVarDeclInterface() { return false; }
173173
static bool stackSaveOp() { return false; }
174174
static bool aggValueSlot() { return false; }
175+
static bool aggValueSlotVolatile() { return false; }
176+
static bool aggValueSlotDestructedFlag() { return false; }
177+
static bool aggValueSlotAlias() { return false; }
178+
static bool aggValueSlotGC() { return false; }
175179
static bool generateDebugInfo() { return false; }
176180
static bool pointerOverflowSanitizer() { return false; }
177181
static bool fpConstraints() { return false; }
@@ -225,6 +229,7 @@ struct MissingFeatures {
225229
static bool isMemcpyEquivalentSpecialMember() { return false; }
226230
static bool isTrivialCtorOrDtor() { return false; }
227231
static bool implicitConstructorArgs() { return false; }
232+
static bool lowerAggregateLoadStore() { return false; }
228233

229234
// Missing types
230235
static bool dataMemberType() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,18 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
321321
return Address(baseAddr, destType, addr.getAlignment());
322322
}
323323

324+
/// Cast the element type of the given address to a different type,
325+
/// preserving information like the alignment.
326+
Address createElementBitCast(mlir::Location loc, Address addr,
327+
mlir::Type destType) {
328+
if (destType == addr.getElementType())
329+
return addr;
330+
331+
auto ptrTy = getPointerTo(destType);
332+
return Address(createBitcast(loc, addr.getPointer(), ptrTy), destType,
333+
addr.getAlignment());
334+
}
335+
324336
cir::LoadOp createLoad(mlir::Location loc, Address addr,
325337
bool isVolatile = false) {
326338
mlir::IntegerAttr align = getAlignmentAttr(addr.getAlignment());

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,23 @@ CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
6060
return *this;
6161
}
6262

63+
void CIRGenFunction::emitAggregateStore(mlir::Value value, Address dest) {
64+
// In classic codegen:
65+
// Function to store a first-class aggregate into memory. We prefer to
66+
// store the elements rather than the aggregate to be more friendly to
67+
// fast-isel.
68+
// In CIR codegen:
69+
// Emit the most simple cir.store possible (e.g. a store for a whole
70+
// record), which can later be broken down in other CIR levels (or prior
71+
// to dialect codegen).
72+
73+
// Stored result for the callers of this function expected to be in the same
74+
// scope as the value, don't make assumptions about current insertion point.
75+
mlir::OpBuilder::InsertionGuard guard(builder);
76+
builder.setInsertionPointAfter(value.getDefiningOp());
77+
builder.createStore(*currSrcLoc, value, dest);
78+
}
79+
6380
/// Returns the canonical formal type of the given C++ method.
6481
static CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *md) {
6582
return md->getType()
@@ -399,8 +416,48 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
399416
assert(!cir::MissingFeatures::opCallBitcastArg());
400417
cirCallArgs[argNo] = v;
401418
} else {
402-
assert(!cir::MissingFeatures::opCallAggregateArgs());
403-
cgm.errorNYI("emitCall: aggregate function call argument");
419+
Address src = Address::invalid();
420+
if (!arg.isAggregate())
421+
cgm.errorNYI(loc, "emitCall: non-aggregate call argument");
422+
else
423+
src = arg.hasLValue() ? arg.getKnownLValue().getAddress()
424+
: arg.getKnownRValue().getAggregateAddress();
425+
426+
// Fast-isel and the optimizer generally like scalar values better than
427+
// FCAs, so we flatten them if this is safe to do for this argument.
428+
auto argRecordTy = cast<cir::RecordType>(argType);
429+
mlir::Type srcTy = src.getElementType();
430+
// FIXME(cir): get proper location for each argument.
431+
mlir::Location argLoc = loc;
432+
433+
// If the source type is smaller than the destination type of the
434+
// coerce-to logic, copy the source value into a temp alloca the size
435+
// of the destination type to allow loading all of it. The bits past
436+
// the source value are left undef.
437+
// FIXME(cir): add data layout info and compare sizes instead of
438+
// matching the types.
439+
//
440+
// uint64_t SrcSize = CGM.getDataLayout().getTypeAllocSize(SrcTy);
441+
// uint64_t DstSize = CGM.getDataLayout().getTypeAllocSize(STy);
442+
// if (SrcSize < DstSize) {
443+
if (srcTy != argRecordTy) {
444+
cgm.errorNYI(loc, "emitCall: source type does not match argument type");
445+
} else {
446+
// FIXME(cir): this currently only runs when the types are different,
447+
// but should be when alloc sizes are different, fix this as soon as
448+
// datalayout gets introduced.
449+
src = builder.createElementBitCast(argLoc, src, argRecordTy);
450+
}
451+
452+
// assert(NumCIRArgs == STy.getMembers().size());
453+
// In LLVMGen: Still only pass the struct without any gaps but mark it
454+
// as such somehow.
455+
//
456+
// In CIRGen: Emit a load from the "whole" struct,
457+
// which shall be broken later by some lowering step into multiple
458+
// loads.
459+
assert(!cir::MissingFeatures::lowerAggregateLoadStore());
460+
cirCallArgs[argNo] = builder.createLoad(argLoc, src);
404461
}
405462
}
406463

@@ -439,6 +496,7 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
439496

440497
assert(!cir::MissingFeatures::opCallAttrs());
441498

499+
mlir::Location callLoc = loc;
442500
cir::CIRCallOpInterface theCall = emitCallLikeOp(
443501
*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, cirCallArgs);
444502

@@ -452,6 +510,19 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
452510
if (isa<cir::VoidType>(retCIRTy))
453511
return getUndefRValue(retTy);
454512
switch (getEvaluationKind(retTy)) {
513+
case cir::TEK_Aggregate: {
514+
Address destPtr = returnValue.getValue();
515+
516+
if (!destPtr.isValid())
517+
destPtr = createMemTemp(retTy, callLoc, getCounterAggTmpAsString());
518+
519+
mlir::ResultRange results = theCall->getOpResults();
520+
assert(results.size() <= 1 && "multiple returns from a call");
521+
522+
SourceLocRAIIObject loc{*this, callLoc};
523+
emitAggregateStore(results[0], destPtr);
524+
return RValue::getAggregate(destPtr);
525+
}
455526
case cir::TEK_Scalar: {
456527
mlir::ResultRange results = theCall->getOpResults();
457528
assert(results.size() == 1 && "unexpected number of returns");
@@ -468,7 +539,6 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
468539
return RValue::get(results[0]);
469540
}
470541
case cir::TEK_Complex:
471-
case cir::TEK_Aggregate:
472542
cgm.errorNYI(loc, "unsupported evaluation kind of function call result");
473543
return getUndefRValue(retTy);
474544
}
@@ -487,10 +557,21 @@ void CIRGenFunction::emitCallArg(CallArgList &args, const clang::Expr *e,
487557

488558
bool hasAggregateEvalKind = hasAggregateEvaluationKind(argType);
489559

490-
if (hasAggregateEvalKind) {
491-
assert(!cir::MissingFeatures::opCallAggregateArgs());
492-
cgm.errorNYI(e->getSourceRange(),
493-
"emitCallArg: aggregate function call argument");
560+
// In the Microsoft C++ ABI, aggregate arguments are destructed by the callee.
561+
// However, we still have to push an EH-only cleanup in case we unwind before
562+
// we make it to the call.
563+
if (argType->isRecordType() &&
564+
argType->castAs<RecordType>()->getDecl()->isParamDestroyedInCallee()) {
565+
assert(!cir::MissingFeatures::msabi());
566+
cgm.errorNYI(e->getSourceRange(), "emitCallArg: msabi is NYI");
567+
}
568+
569+
if (hasAggregateEvalKind && isa<ImplicitCastExpr>(e) &&
570+
cast<CastExpr>(e)->getCastKind() == CK_LValueToRValue) {
571+
LValue lv = emitLValue(cast<CastExpr>(e)->getSubExpr());
572+
assert(lv.isSimple());
573+
args.addUncopiedAggregate(lv, argType);
574+
return;
494575
}
495576

496577
args.add(emitAnyExprToTemp(e), argType);
@@ -511,12 +592,13 @@ QualType CIRGenFunction::getVarArgType(const Expr *arg) {
511592
/// Similar to emitAnyExpr(), however, the result will always be accessible
512593
/// even if no aggregate location is provided.
513594
RValue CIRGenFunction::emitAnyExprToTemp(const Expr *e) {
514-
assert(!cir::MissingFeatures::opCallAggregateArgs());
595+
AggValueSlot aggSlot = AggValueSlot::ignored();
515596

516597
if (hasAggregateEvaluationKind(e->getType()))
517-
cgm.errorNYI(e->getSourceRange(), "emit aggregate value to temp");
598+
aggSlot = createAggTemp(e->getType(), getLoc(e->getSourceRange()),
599+
getCounterAggTmpAsString());
518600

519-
return emitAnyExpr(e);
601+
return emitAnyExpr(e, aggSlot);
520602
}
521603

522604
void CIRGenFunction::emitCallArgs(

clang/lib/CIR/CodeGen/CIRGenCall.h

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,16 @@ struct CallArg {
105105
CallArg(RValue rv, clang::QualType ty)
106106
: rv(rv), hasLV(false), isUsed(false), ty(ty) {}
107107

108+
CallArg(LValue lv, clang::QualType ty)
109+
: lv(lv), hasLV(true), isUsed(false), ty(ty) {}
110+
108111
bool hasLValue() const { return hasLV; }
109112

113+
LValue getKnownLValue() const {
114+
assert(hasLV && !isUsed);
115+
return lv;
116+
}
117+
110118
RValue getKnownRValue() const {
111119
assert(!hasLV && !isUsed);
112120
return rv;
@@ -119,6 +127,10 @@ class CallArgList : public llvm::SmallVector<CallArg, 8> {
119127
public:
120128
void add(RValue rvalue, clang::QualType type) { emplace_back(rvalue, type); }
121129

130+
void addUncopiedAggregate(LValue lvalue, clang::QualType type) {
131+
emplace_back(lvalue, type);
132+
}
133+
122134
/// Add all the arguments from another CallArgList to this one. After doing
123135
/// this, the old CallArgList retains its list of arguments, but must not
124136
/// be used to emit a call.
@@ -134,7 +146,15 @@ class CallArgList : public llvm::SmallVector<CallArg, 8> {
134146

135147
/// Contains the address where the return value of a function can be stored, and
136148
/// whether the address is volatile or not.
137-
class ReturnValueSlot {};
149+
class ReturnValueSlot {
150+
Address addr = Address::invalid();
151+
152+
public:
153+
ReturnValueSlot() = default;
154+
ReturnValueSlot(Address addr) : addr(addr) {}
155+
156+
Address getValue() const { return addr; }
157+
};
138158

139159
} // namespace clang::CIRGen
140160

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,16 +1010,20 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
10101010

10111011
/// Emit code to compute the specified expression which
10121012
/// can have any type. The result is returned as an RValue struct.
1013-
RValue CIRGenFunction::emitAnyExpr(const Expr *e) {
1013+
RValue CIRGenFunction::emitAnyExpr(const Expr *e, AggValueSlot aggSlot) {
10141014
switch (CIRGenFunction::getEvaluationKind(e->getType())) {
10151015
case cir::TEK_Scalar:
10161016
return RValue::get(emitScalarExpr(e));
10171017
case cir::TEK_Complex:
10181018
cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: complex type");
10191019
return RValue::get(nullptr);
1020-
case cir::TEK_Aggregate:
1021-
cgm.errorNYI(e->getSourceRange(), "emitAnyExpr: aggregate type");
1022-
return RValue::get(nullptr);
1020+
case cir::TEK_Aggregate: {
1021+
if (aggSlot.isIgnored())
1022+
aggSlot = createAggTemp(e->getType(), getLoc(e->getSourceRange()),
1023+
getCounterAggTmpAsString());
1024+
emitAggExpr(e, aggSlot);
1025+
return aggSlot.asRValue();
1026+
}
10231027
}
10241028
llvm_unreachable("bad evaluation kind");
10251029
}

clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,15 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
2828
CIRGenFunction &cgf;
2929
AggValueSlot dest;
3030

31+
// Calls `fn` with a valid return value slot, potentially creating a temporary
32+
// to do so. If a temporary is created, an appropriate copy into `Dest` will
33+
// be emitted, as will lifetime markers.
34+
//
35+
// The given function should take a ReturnValueSlot, and return an RValue that
36+
// points to said slot.
37+
void withReturnValueSlot(const Expr *e,
38+
llvm::function_ref<RValue(ReturnValueSlot)> fn);
39+
3140
AggValueSlot ensureSlot(mlir::Location loc, QualType t) {
3241
if (!dest.isIgnored())
3342
return dest;
@@ -40,16 +49,28 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
4049
AggExprEmitter(CIRGenFunction &cgf, AggValueSlot dest)
4150
: cgf(cgf), dest(dest) {}
4251

52+
/// Given an expression with aggregate type that represents a value lvalue,
53+
/// this method emits the address of the lvalue, then loads the result into
54+
/// DestPtr.
55+
void emitAggLoadOfLValue(const Expr *e);
56+
4357
void emitArrayInit(Address destPtr, cir::ArrayType arrayTy, QualType arrayQTy,
4458
Expr *exprToVisit, ArrayRef<Expr *> args,
4559
Expr *arrayFiller);
4660

61+
/// Perform the final copy to DestPtr, if desired.
62+
void emitFinalDestCopy(QualType type, const LValue &src);
63+
4764
void emitInitializationToLValue(Expr *e, LValue lv);
4865

4966
void emitNullInitializationToLValue(mlir::Location loc, LValue lv);
5067

5168
void Visit(Expr *e) { StmtVisitor<AggExprEmitter>::Visit(e); }
5269

70+
void VisitCallExpr(const CallExpr *e);
71+
72+
void VisitDeclRefExpr(DeclRefExpr *e) { emitAggLoadOfLValue(e); }
73+
5374
void VisitInitListExpr(InitListExpr *e);
5475
void VisitCXXConstructExpr(const CXXConstructExpr *e);
5576

@@ -80,6 +101,17 @@ static bool isTrivialFiller(Expr *e) {
80101
return false;
81102
}
82103

104+
/// Given an expression with aggregate type that represents a value lvalue, this
105+
/// method emits the address of the lvalue, then loads the result into DestPtr.
106+
void AggExprEmitter::emitAggLoadOfLValue(const Expr *e) {
107+
LValue lv = cgf.emitLValue(e);
108+
109+
// If the type of the l-value is atomic, then do an atomic load.
110+
assert(!cir::MissingFeatures::opLoadStoreAtomic());
111+
112+
emitFinalDestCopy(e->getType(), lv);
113+
}
114+
83115
void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy,
84116
QualType arrayQTy, Expr *e,
85117
ArrayRef<Expr *> args, Expr *arrayFiller) {
@@ -182,6 +214,18 @@ void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy,
182214
}
183215
}
184216

217+
/// Perform the final copy to destPtr, if desired.
218+
void AggExprEmitter::emitFinalDestCopy(QualType type, const LValue &src) {
219+
// If dest is ignored, then we're evaluating an aggregate expression
220+
// in a context that doesn't care about the result. Note that loads
221+
// from volatile l-values force the existence of a non-ignored
222+
// destination.
223+
if (dest.isIgnored())
224+
return;
225+
226+
cgf.cgm.errorNYI("emitFinalDestCopy: non-ignored dest is NYI");
227+
}
228+
185229
void AggExprEmitter::emitInitializationToLValue(Expr *e, LValue lv) {
186230
const QualType type = lv.getType();
187231

@@ -246,6 +290,44 @@ void AggExprEmitter::emitNullInitializationToLValue(mlir::Location loc,
246290
cgf.emitNullInitialization(loc, lv.getAddress(), lv.getType());
247291
}
248292

293+
void AggExprEmitter::VisitCallExpr(const CallExpr *e) {
294+
if (e->getCallReturnType(cgf.getContext())->isReferenceType()) {
295+
cgf.cgm.errorNYI(e->getSourceRange(), "reference return type");
296+
return;
297+
}
298+
299+
withReturnValueSlot(
300+
e, [&](ReturnValueSlot slot) { return cgf.emitCallExpr(e, slot); });
301+
}
302+
303+
void AggExprEmitter::withReturnValueSlot(
304+
const Expr *e, llvm::function_ref<RValue(ReturnValueSlot)> fn) {
305+
QualType retTy = e->getType();
306+
307+
assert(!cir::MissingFeatures::aggValueSlotDestructedFlag());
308+
bool requiresDestruction =
309+
retTy.isDestructedType() == QualType::DK_nontrivial_c_struct;
310+
if (requiresDestruction)
311+
cgf.cgm.errorNYI(
312+
e->getSourceRange(),
313+
"withReturnValueSlot: return value requiring destruction is NYI");
314+
315+
// If it makes no observable difference, save a memcpy + temporary.
316+
//
317+
// We need to always provide our own temporary if destruction is required.
318+
// Otherwise, fn will emit its own, notice that it's "unused", and end its
319+
// lifetime before we have the chance to emit a proper destructor call.
320+
assert(!cir::MissingFeatures::aggValueSlotAlias());
321+
assert(!cir::MissingFeatures::aggValueSlotGC());
322+
323+
Address retAddr = dest.getAddress();
324+
assert(!cir::MissingFeatures::emitLifetimeMarkers());
325+
326+
assert(!cir::MissingFeatures::aggValueSlotVolatile());
327+
assert(!cir::MissingFeatures::aggValueSlotDestructedFlag());
328+
fn(ReturnValueSlot(retAddr));
329+
}
330+
249331
void AggExprEmitter::VisitInitListExpr(InitListExpr *e) {
250332
if (e->hadArrayRangeDesignator())
251333
llvm_unreachable("GNU array range designator extension");

0 commit comments

Comments
 (0)