-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[CIR] Function calls with aggregate arguments and return values #143377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we do this now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is data layout info available in upstream yet? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DataLayout is there, but getTypeAllocSize hasn't been upstreamed yet. This can wait, but let's not forget about it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a new missing feature flag for tracking this. |
||
// 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. | ||
Lancern marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 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()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm adding a couple of these in #143932. We should revisit this after both PRs are merged. |
||
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"); | ||
|
Uh oh!
There was an error while loading. Please reload this page.