10#include "mlir/IR/Builders.h"
11#include "mlir/IR/Dominance.h"
17using namespace mlir::abi;
47bool needsRewrite(
const FunctionClassification &fc) {
52 if ((fc.returnInfo.kind != ArgKind::Direct) || fc.returnInfo.coercedType)
54 for (
const ArgClassification &ac : fc.argInfos)
55 if ((ac.kind != ArgKind::Direct) || ac.coercedType)
72 if (ac.kind != ArgKind::Direct || !ac.coercedType || !ac.canFlatten)
74 auto recTy = dyn_cast<cir::RecordType>(ac.coercedType);
75 if (!recTy || !recTy.isStruct() || recTy.getNumElements() <= 1)
87 const FunctionClassification &fc,
88 SmallVectorImpl<mlir::Type> &newArgTypes,
89 function_ref<mlir::InFlightDiagnostic()> emitError) {
90 assert(newArgTypes.empty() &&
"expected an empty output vector");
91 newArgTypes.reserve(oldArgTypes.size());
92 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
93 mlir::Type origTy = oldArgTypes[idx];
101 llvm::append_range(newArgTypes, flatTy.getMembers());
107 newArgTypes.push_back(ac.coercedType ? ac.coercedType : origTy);
110 case ArgKind::Ignore:
112 case ArgKind::Expand: {
116 auto recTy = cast<cir::RecordType>(origTy);
117 assert(recTy.isStruct() &&
118 "Expand classification requires a struct type, not a union");
119 assert(!recTy.getMembers().empty() &&
120 "Expand classification requires at least one struct field");
121 llvm::append_range(newArgTypes, recTy.getMembers());
124 case ArgKind::Extend:
131 newArgTypes.push_back(origTy);
133 case ArgKind::Indirect:
139 newArgTypes.push_back(cir::PointerType::get(origTy));
143 return mlir::success();
151computeNewReturnType(mlir::Type origRetTy,
const ArgClassification &retInfo,
152 mlir::MLIRContext *ctx,
153 function_ref<mlir::InFlightDiagnostic()> emitError) {
154 switch (retInfo.kind) {
155 case ArgKind::Direct:
158 return retInfo.coercedType ? retInfo.coercedType : origRetTy;
159 case ArgKind::Ignore:
160 return cir::VoidType::get(ctx);
161 case ArgKind::Expand:
162 emitError() <<
"Expand return is not allowed (classic codegen rejects "
163 <<
"it in EmitFunctionEpilog)";
165 case ArgKind::Extend:
170 case ArgKind::Indirect:
175 return cir::VoidType::get(ctx);
177 llvm_unreachable(
"all ArgKind cases handled");
185mlir::Value createIgnoredValue(mlir::OpBuilder &builder, mlir::Location loc,
187 return cir::ConstantOp::create(builder, loc, ty, cir::PoisonAttr::get(ty));
195mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx,
197 mlir::ArrayAttr existingArgAttrs,
198 const FunctionClassification &fc) {
199 mlir::Builder builder(ctx);
201 newArgAttrs.reserve(fc.argInfos.size());
202 for (
auto [oldIdx, ac] : llvm::enumerate(fc.argInfos)) {
203 if (ac.kind == ArgKind::Ignore)
205 mlir::DictionaryAttr existing = builder.getDictionaryAttr({});
206 if (existingArgAttrs && oldIdx < existingArgAttrs.size())
207 existing = mlir::cast<mlir::DictionaryAttr>(existingArgAttrs[oldIdx]);
211 newArgAttrs.append(flatTy.getNumElements(),
212 builder.getDictionaryAttr({}));
213 }
else if (ac.kind == ArgKind::Expand) {
216 auto recTy = cast<cir::RecordType>(origArgTypes[oldIdx]);
217 newArgAttrs.append(recTy.getNumElements(), builder.getDictionaryAttr({}));
218 }
else if (ac.kind == ArgKind::Extend) {
219 StringRef attrName = ac.signExtend ?
"llvm.signext" :
"llvm.zeroext";
221 attrs.push_back(builder.getNamedAttr(attrName, builder.getUnitAttr()));
222 newArgAttrs.push_back(builder.getDictionaryAttr(attrs));
223 }
else if (ac.kind == ArgKind::Indirect) {
240 mlir::Type pointeeTy = origArgTypes[oldIdx];
241 StringRef ownershipAttr = ac.byVal ?
"llvm.byval" :
"llvm.byref";
243 attrs.push_back(builder.getNamedAttr(
244 "llvm.align", builder.getI64IntegerAttr(ac.indirectAlign.value())));
246 builder.getNamedAttr(ownershipAttr, mlir::TypeAttr::get(pointeeTy)));
249 builder.getNamedAttr(
"llvm.noalias", builder.getUnitAttr()));
251 builder.getNamedAttr(
"llvm.noundef", builder.getUnitAttr()));
253 newArgAttrs.push_back(builder.getDictionaryAttr(attrs));
255 newArgAttrs.push_back(existing);
258 return builder.getArrayAttr(newArgAttrs);
264mlir::ArrayAttr updateResAttrs(mlir::MLIRContext *ctx,
265 mlir::ArrayAttr existingResAttrs,
266 const ArgClassification &retInfo) {
267 if (retInfo.kind != ArgKind::Extend)
268 return existingResAttrs;
271 if (existingResAttrs && !existingResAttrs.empty())
272 for (mlir::NamedAttribute na :
273 mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0]))
275 StringRef attrName = retInfo.signExtend ?
"llvm.signext" :
"llvm.zeroext";
276 attrs.push_back(mlir::NamedAttribute(mlir::StringAttr::get(ctx, attrName),
277 mlir::UnitAttr::get(ctx)));
278 return mlir::ArrayAttr::get(ctx, {mlir::DictionaryAttr::get(ctx, attrs)});
306emitCoercionToMemory(mlir::OpBuilder &builder, mlir::Location loc,
307 mlir::Type dstTy, mlir::Value src,
308 mlir::FunctionOpInterface funcOp,
309 const mlir::DataLayout &dl,
310 SmallPtrSetImpl<mlir::Operation *> &createdOps) {
311 mlir::Type srcTy = src.getType();
312 assert(srcTy != dstTy &&
313 "emitCoercion callers must pre-check that the types differ");
315 uint64_t srcAlign = dl.getTypeABIAlignment(srcTy);
316 uint64_t dstAlign = dl.getTypeABIAlignment(dstTy);
317 uint64_t allocaAlign = std::max(srcAlign, dstAlign);
319 dl.getTypeSize(srcTy) >= dl.getTypeSize(dstTy) ? srcTy : dstTy;
321 auto slotPtrTy = cir::PointerType::get(slotTy);
322 auto srcPtrTy = cir::PointerType::get(srcTy);
323 auto dstPtrTy = cir::PointerType::get(dstTy);
325 cir::AllocaOp alloca;
327 mlir::OpBuilder::InsertionGuard guard(builder);
328 mlir::Block &entry = funcOp->getRegion(0).front();
329 builder.setInsertionPointToStart(&entry);
330 alloca = cir::AllocaOp::create(builder, loc, slotPtrTy,
331 builder.getStringAttr(
"coerce"),
332 builder.getI64IntegerAttr(allocaAlign));
334 createdOps.insert(alloca);
337 mlir::Value srcSlot = alloca;
338 if (slotTy != srcTy) {
339 auto srcCast = cir::CastOp::create(builder, loc, srcPtrTy,
340 cir::CastKind::bitcast, alloca);
341 createdOps.insert(srcCast);
344 auto store = cir::StoreOp::create(builder, loc, src, srcSlot);
345 createdOps.insert(store);
348 if (slotTy != dstTy) {
349 auto dstCast = cir::CastOp::create(builder, loc, dstPtrTy,
350 cir::CastKind::bitcast, alloca);
351 createdOps.insert(dstCast);
360mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
361 mlir::Type dstTy, mlir::Value src,
362 mlir::FunctionOpInterface funcOp,
363 const mlir::DataLayout &dl,
364 SmallPtrSetImpl<mlir::Operation *> &createdOps) {
365 mlir::Value dstSlot =
366 emitCoercionToMemory(builder, loc, dstTy, src, funcOp, dl, createdOps);
367 auto load = cir::LoadOp::create(builder, loc, dstSlot);
368 createdOps.insert(load);
374mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
375 mlir::Type dstTy, mlir::Value src,
376 mlir::FunctionOpInterface funcOp,
377 const mlir::DataLayout &dl) {
378 SmallPtrSet<mlir::Operation *, 4> ignored;
379 return emitCoercion(builder, loc, dstTy, src, funcOp, dl, ignored);
384void insertReturnCoercion(mlir::FunctionOpInterface funcOp,
385 mlir::Type origRetTy, mlir::Type coercedRetTy,
386 mlir::OpBuilder &builder,
387 const mlir::DataLayout &dl) {
389 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
390 for (cir::ReturnOp r : returns) {
391 if (r.getInput().empty())
393 mlir::Value origVal = r.getInput()[0];
394 if (origVal.getType() == coercedRetTy)
396 builder.setInsertionPoint(r);
397 mlir::Value coerced =
398 emitCoercion(builder, r.getLoc(), coercedRetTy, origVal, funcOp, dl);
399 r->setOperand(0, coerced);
415emitStructFieldArgs(mlir::OpBuilder &builder, mlir::Location loc,
417 SmallVectorImpl<mlir::Value> &newArgs,
418 SmallVectorImpl<cir::LoadOp> &replacedWholeLoads) {
419 cir::LoadOp wholeLoad = structVal.getDefiningOp<cir::LoadOp>();
420 cir::AllocaOp srcAlloca;
421 if (wholeLoad && !wholeLoad.getIsVolatile() && !wholeLoad.getMemOrder())
422 srcAlloca = wholeLoad.getAddr().getDefiningOp<cir::AllocaOp>();
425 mlir::OpBuilder::InsertionGuard guard(builder);
426 builder.setInsertionPoint(wholeLoad);
427 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
428 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
429 mlir::Value fieldPtr = cir::GetMemberOp::create(
430 builder, loc, fieldPtrTy, srcAlloca,
"", f);
431 newArgs.push_back(cir::LoadOp::create(builder, loc, fieldPtr));
433 replacedWholeLoads.push_back(wholeLoad);
437 cir::ExtractMemberOp::create(builder, loc, structVal, f));
458void insertArgCoercion(mlir::FunctionOpInterface funcOp,
459 const FunctionClassification &fc,
460 mlir::OpBuilder &builder,
const mlir::DataLayout &dl,
462 mlir::Region &body = funcOp->getRegion(0);
465 mlir::Block &entry = body.front();
471 unsigned blockArgIdx = hasSRetArg ? 1 : 0;
473 for (
const ArgClassification &ac : fc.argInfos) {
474 assert(blockArgIdx < entry.getNumArguments() &&
475 "classification count must not exceed entry block arguments");
477 if (ac.kind == ArgKind::Expand) {
481 mlir::BlockArgument origArg = entry.getArgument(blockArgIdx);
482 auto recTy = cast<cir::RecordType>(origArg.getType());
484 "Expand classification requires a struct type, not a union");
486 assert(numFields > 0 &&
487 "Expand classification requires at least one struct field");
488 mlir::Location loc = funcOp.getLoc();
498 cir::StoreOp paramStore;
499 cir::AllocaOp destAlloca;
500 if (!origArg.use_empty()) {
501 assert(origArg.hasOneUse() &&
502 "Expand arg must have exactly one use (the CIRGen param spill)");
503 paramStore = cast<cir::StoreOp>(*origArg.user_begin());
504 assert(paramStore.getValue() == origArg &&
505 "Expand arg's use must be the value operand of its store");
506 destAlloca = cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
513 mlir::Operation *fieldStoreInsertPt =
nullptr;
515 fieldStoreInsertPt = paramStore->getNextNode();
516 assert(fieldStoreInsertPt &&
517 "param spill must be followed by a block terminator");
529 builder.setInsertionPoint(fieldStoreInsertPt);
530 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
532 origArg.setType(fieldTy);
534 entry.insertArgument(blockArgIdx + f, fieldTy, loc);
537 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
538 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
541 cir::StoreOp::create(builder, loc, entry.getArgument(blockArgIdx + f),
545 blockArgIdx += numFields;
549 mlir::BlockArgument blockArg = entry.getArgument(blockArgIdx);
558 unsigned numFields = flatTy.getNumElements();
559 assert(numFields >= 2 &&
"getFlattenedCoercedType guarantees >1 fields");
560 Type origTy = blockArg.getType();
561 Location loc = funcOp.getLoc();
564 blockArg.setType(flatTy.getElementType(0));
565 for (
unsigned f = 1; f < numFields; ++f)
566 entry.insertArgument(blockArgIdx + f, flatTy.getElementType(f), loc);
569 builder.setInsertionPointToStart(&entry);
570 auto flatPtrTy = cir::PointerType::get(flatTy);
571 uint64_t flatAlign = dl.getTypeABIAlignment(flatTy);
572 auto flatSlot = cir::AllocaOp::create(
573 builder, loc, flatPtrTy, builder.getStringAttr(
"coerce"),
574 builder.getI64IntegerAttr(flatAlign));
575 SmallPtrSet<Operation *, 8> flattenOps = {flatSlot};
576 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
577 Type fieldPtrTy = cir::PointerType::get(fieldTy);
578 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
581 flattenOps.insert(fieldPtr);
582 auto storeOp = cir::StoreOp::create(
583 builder, loc, entry.getArgument(blockArgIdx + f), fieldPtr);
584 flattenOps.insert(storeOp);
587 cir::LoadOp::create(builder, loc, flatTy, flatSlot.getResult());
588 flattenOps.insert(flatLoaded);
592 Value finalVal = flatLoaded;
593 if (origTy != flatTy) {
594 SmallPtrSet<Operation *, 4> coercionOps;
595 finalVal = emitCoercion(builder, loc, origTy, flatLoaded, funcOp, dl,
597 flattenOps.insert(coercionOps.begin(), coercionOps.end());
602 blockArg.replaceAllUsesExcept(finalVal, flattenOps);
604 blockArgIdx += numFields;
608 if (ac.kind == ArgKind::Direct && ac.coercedType) {
609 mlir::Type oldArgTy = blockArg.getType();
610 mlir::Type newArgTy = ac.coercedType;
611 if (oldArgTy == newArgTy) {
615 blockArg.setType(newArgTy);
617 builder.setInsertionPointToStart(&entry);
618 SmallPtrSet<mlir::Operation *, 4> coercionOps;
619 mlir::Value adapted = emitCoercion(builder, funcOp.getLoc(), oldArgTy,
620 blockArg, funcOp, dl, coercionOps);
626 blockArg.replaceAllUsesExcept(adapted, coercionOps);
627 }
else if (ac.kind == ArgKind::Indirect) {
632 auto ptrTy = cir::PointerType::get(blockArg.getType());
645 cir::StoreOp paramStore;
646 cir::AllocaOp destAlloca;
647 if (!blockArg.use_empty()) {
648 assert(blockArg.hasOneUse() &&
649 "byref arg must have exactly one use (the CIRGen param "
651 paramStore = cast<cir::StoreOp>(*blockArg.user_begin());
652 assert(paramStore.getValue() == blockArg &&
653 "byref arg's use must be the value operand of its store");
655 cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
662 blockArg.setType(ptrTy);
665 destAlloca.getResult().replaceAllUsesWith(blockArg);
671 blockArg.setType(ptrTy);
673 builder.setInsertionPointToStart(&entry);
674 auto loadOp = cir::LoadOp::create(builder, funcOp.getLoc(), blockArg);
675 SmallPtrSet<mlir::Operation *, 1> loadOps = {loadOp};
676 blockArg.replaceAllUsesExcept(loadOp.getResult(), loadOps);
713void insertSRetStores(mlir::FunctionOpInterface funcOp, mlir::Type origRetTy,
714 mlir::OpBuilder &builder) {
715 mlir::Value sretPtr = funcOp.getArguments()[0];
718 funcOp->walk([&](cir::ReturnOp retOp) { returnOps.push_back(retOp); });
720 cir::AllocaOp retAlloca =
nullptr;
721 for (cir::ReturnOp retOp : returnOps) {
724 assert(!retOp.getInput().empty() &&
725 "cir.return in sret function must have an operand");
727 cir::LoadOp retLoad =
728 mlir::cast<cir::LoadOp>(retOp.getInput()[0].getDefiningOp());
736 retAlloca = mlir::cast<cir::AllocaOp>(retLoad.getAddr().getDefiningOp());
737 retAlloca.getResult().replaceAllUsesWith(sretPtr);
743 builder.setInsertionPoint(retOp);
744 cir::ReturnOp::create(builder, retOp.getLoc());
746 if (retLoad.use_empty())
765 builder.getNamedAttr(
"llvm.sret", mlir::TypeAttr::get(retTy)));
767 builder.getNamedAttr(
"llvm.align", builder.getI64IntegerAttr(align)));
770 builder.getNamedAttr(
"llvm.noalias", builder.getUnitAttr()));
771 attrs.push_back(builder.getNamedAttr(
"llvm.writable", builder.getUnitAttr()));
773 builder.getNamedAttr(
"llvm.dead_on_unwind", builder.getUnitAttr()));
783void applySretSlotAttrs(cir::CallOp newCall, mlir::ArrayAttr argAttrs,
784 mlir::Type retTy, uint64_t align,
785 mlir::OpBuilder &builder) {
786 mlir::MLIRContext *ctx = newCall->getContext();
788 buildSretSlotAttrs(builder, retTy, align,
false);
791 newArgAttrs.reserve(newCall.getArgOperands().size());
792 newArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
794 llvm::append_range(newArgAttrs, argAttrs);
795 assert(newArgAttrs.size() <= newCall.getArgOperands().size() &&
796 "arg_attrs wider than the rewritten call's operand list");
797 newArgAttrs.resize(newCall.getArgOperands().size(),
798 mlir::DictionaryAttr::get(ctx));
799 newCall->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, newArgAttrs));
809void rewriteIndirectReturnCall(cir::CallOp call,
810 const FunctionClassification &fc,
812 mlir::Type origRetTy,
814 mlir::OpBuilder &builder) {
815 mlir::MLIRContext *ctx = call->getContext();
816 auto ptrTy = cir::PointerType::get(origRetTy);
817 builder.setInsertionPoint(call);
818 uint64_t sretAlign = fc.returnInfo.indirectAlign.value();
831 mlir::Value sretSlot =
nullptr;
832 cir::StoreOp reuseStore =
nullptr;
833 if (call.getResult().hasOneUse()) {
834 mlir::Operation *user = *call.getResult().getUsers().begin();
835 if (
auto store = mlir::dyn_cast<cir::StoreOp>(user))
836 if (store.getValue() == call.getResult() &&
837 store.getAddr().getType() == ptrTy &&
838 mlir::DominanceInfo().properlyDominates(store.getAddr(), call)) {
839 sretSlot = store.getAddr();
844 auto alloca = cir::AllocaOp::create(
845 builder, call.getLoc(), ptrTy,
846 builder.getStringAttr(
"sret"),
847 builder.getI64IntegerAttr(sretAlign));
852 sretArgs.push_back(sretSlot);
853 sretArgs.append(newArgs.begin(), newArgs.end());
855 mlir::Type sretVoidTy = cir::VoidType::get(ctx);
856 auto newCall = cir::CallOp::create(
857 builder, call.getLoc(), call.getCalleeAttr(), sretVoidTy, sretArgs);
858 for (mlir::NamedAttribute attr : call->getAttrs())
859 if (!newCall->hasAttr(
attr.getName()))
860 newCall->setAttr(
attr.getName(),
attr.getValue());
867 mlir::ArrayAttr argAttrs = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
868 bool needsArgAttrUpdate =
869 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
870 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
871 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
872 getFlattenedCoercedType(ac);
874 if (needsArgAttrUpdate)
875 argAttrs = updateArgAttrs(ctx, origCallArgTypes, argAttrs, fc);
876 applySretSlotAttrs(newCall, argAttrs, origRetTy, sretAlign, builder);
884 builder.setInsertionPointAfter(newCall);
885 auto load = cir::LoadOp::create(builder, call.getLoc(), origRetTy, sretSlot,
890 cir::SyncScopeKindAttr(),
893 call.getResult().replaceAllUsesWith(load);
901 mlir::FunctionOpInterface funcOpInterface,
const FunctionClassification &fc,
902 mlir::OpBuilder &builder) {
908 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(funcOpInterface);
910 if (!needsRewrite(fc))
911 return mlir::success();
915 mlir::MLIRContext *ctx = funcOp->getContext();
920 assert(oldResultTypes.size() <= 1 &&
921 "CIR functions return zero or one value");
924 if (mlir::failed(buildNewArgTypes(oldArgTypes, fc, newArgTypes,
925 [&]() {
return funcOp.emitOpError(); })))
926 return mlir::failure();
928 mlir::Type voidTy = cir::VoidType::get(ctx);
929 mlir::Type origRetTy = oldResultTypes.empty() ? voidTy : oldResultTypes[0];
930 mlir::Type newRetTy = computeNewReturnType(
931 origRetTy, fc.returnInfo, ctx, [&]() { return funcOp.emitOpError(); });
933 return mlir::failure();
943 fc.returnInfo.kind == ArgKind::Indirect && !oldResultTypes.empty();
945 newArgTypes.insert(newArgTypes.begin(), cir::PointerType::get(origRetTy));
947 if (funcOp.isDefinition()) {
948 mlir::Region &body = funcOp->getRegion(0);
954 body.front().insertArgument(0u, cir::PointerType::get(origRetTy),
956 insertSRetStores(funcOp, origRetTy, builder);
966 insertArgCoercion(funcOp, fc, builder, dl, hasSRet);
971 if (fc.returnInfo.kind == ArgKind::Direct && fc.returnInfo.coercedType &&
972 !oldResultTypes.empty() && fc.returnInfo.coercedType != origRetTy)
973 insertReturnCoercion(funcOp, origRetTy, fc.returnInfo.coercedType,
976 mlir::Block &entry = body.front();
985 unsigned blockArgIdx = hasSRet ? 1 : 0;
986 for (
auto [i, ac] : llvm::enumerate(fc.argInfos)) {
987 if (blockArgIdx >= entry.getNumArguments())
989 if (ac.kind == ArgKind::Ignore) {
990 mlir::BlockArgument arg = entry.getArgument(blockArgIdx);
991 if (!arg.use_empty()) {
992 builder.setInsertionPointToStart(&entry);
994 createIgnoredValue(builder, funcOp.getLoc(), arg.getType());
995 arg.replaceAllUsesWith(poison);
997 entry.eraseArgument(blockArgIdx);
1001 blockArgIdx += flatTy.getNumElements();
1002 else if (ac.kind == ArgKind::Expand)
1003 blockArgIdx += cast<cir::RecordType>(oldArgTypes[i]).getNumElements();
1015 if (fc.returnInfo.kind == ArgKind::Ignore && !oldResultTypes.empty()) {
1016 assert(mlir::isa<cir::VoidType>(newRetTy) &&
1017 "Ignore-return path requires the new return type to be void");
1019 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
1020 for (cir::ReturnOp r : returns) {
1021 if (r.getNumOperands() == 0)
1023 builder.setInsertionPoint(r);
1024 cir::ReturnOp::create(builder, r.getLoc());
1030 mlir::Type newFnTy = funcOp.cloneTypeWith(newArgTypes, newResultTypes);
1031 funcOp.setFunctionTypeAttr(mlir::TypeAttr::get(newFnTy));
1038 bool needsArgAttrUpdate =
1039 hasSRet || llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1040 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1041 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1042 getFlattenedCoercedType(ac);
1044 if (needsArgAttrUpdate) {
1045 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1046 mlir::ArrayAttr updated = updateArgAttrs(ctx, oldArgTypes, existing, fc);
1052 builder, origRetTy, fc.returnInfo.indirectAlign.value(),
1053 funcOp.isDefinition());
1055 withSret.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1056 llvm::append_range(withSret, updated);
1057 funcOp->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, withSret));
1059 funcOp->setAttr(
"arg_attrs", updated);
1065 if (fc.returnInfo.kind == ArgKind::Extend) {
1066 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1067 funcOp->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1070 return mlir::success();
1075 const FunctionClassification &fc,
1076 mlir::OpBuilder &builder) {
1077 if (!needsRewrite(fc))
1078 return mlir::success();
1080 if (mlir::isa<cir::TryCallOp>(callOp))
1081 return callOp->emitOpError()
1082 <<
"TryCallOp not yet implemented in CallConvLowering";
1084 auto call = mlir::cast<cir::CallOp>(callOp);
1085 if (call.isIndirect())
1086 return call.emitOpError()
1087 <<
"indirect call not yet implemented in CallConvLowering";
1089 mlir::MLIRContext *ctx = callOp->getContext();
1090 auto enclosingFunc = call->getParentOfType<mlir::FunctionOpInterface>();
1092 builder.setInsertionPoint(call);
1095 mlir::ValueRange argOperands = call.getArgOperands();
1096 newArgs.reserve(argOperands.size());
1107 llvm::append_range(origCallArgTypes, argOperands.getTypes());
1108 if (argOperands.size() > fc.argInfos.size())
1109 return call.emitOpError()
1110 <<
"variadic arguments not yet implemented in CallConvLowering";
1111 assert(fc.argInfos.size() == argOperands.size() &&
1112 "call operand count must match classified arg count");
1113 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1114 if (ac.kind == ArgKind::Ignore)
1116 mlir::Value arg = argOperands[idx];
1124 if (arg.getType() != flatTy) {
1125 SmallPtrSet<mlir::Operation *, 4> coercionOps;
1126 mlir::Value coercedPtr =
1127 emitCoercionToMemory(builder, call.getLoc(), flatTy, arg,
1128 enclosingFunc, dl, coercionOps);
1129 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
1130 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
1132 cir::GetMemberOp::create(builder, call.getLoc(), fieldPtrTy,
1134 newArgs.push_back(cir::LoadOp::create(builder, call.getLoc(), fieldTy,
1135 fieldPtr.getResult()));
1138 emitStructFieldArgs(builder, call.getLoc(), arg, flatTy, newArgs,
1139 replacedWholeLoads);
1141 }
else if (ac.kind == ArgKind::Expand) {
1144 auto recTy = cast<cir::RecordType>(arg.getType());
1146 "Expand classification requires a struct type, not a union");
1147 emitStructFieldArgs(builder, call.getLoc(), arg, recTy, newArgs,
1148 replacedWholeLoads);
1149 }
else if (ac.kind == ArgKind::Direct && ac.coercedType &&
1150 arg.getType() != ac.coercedType) {
1151 arg = emitCoercion(builder, call.getLoc(), ac.coercedType, arg,
1153 newArgs.push_back(arg);
1154 }
else if (ac.kind == ArgKind::Indirect) {
1160 mlir::Type argTy = arg.getType();
1161 auto ptrTy = cir::PointerType::get(argTy);
1162 uint64_t align = ac.indirectAlign.value();
1163 StringRef slotName = ac.byVal ?
"byval" :
"byref";
1164 auto slot = cir::AllocaOp::create(builder, call.getLoc(), ptrTy,
1165 builder.getStringAttr(slotName),
1166 builder.getI64IntegerAttr(align));
1167 cir::StoreOp::create(builder, call.getLoc(), arg, slot);
1169 newArgs.push_back(arg);
1171 newArgs.push_back(arg);
1175 bool hasResult = call.getNumResults() > 0;
1176 mlir::Type origRetTy =
1177 hasResult ? call.getResult().getType() : cir::VoidType::get(ctx);
1183 if (fc.returnInfo.kind == ArgKind::Indirect && hasResult) {
1184 rewriteIndirectReturnCall(call, fc, newArgs, origRetTy, origCallArgTypes,
1186 return mlir::success();
1189 mlir::Type callRetTy = origRetTy;
1190 if (fc.returnInfo.kind == ArgKind::Ignore && hasResult)
1191 callRetTy = cir::VoidType::get(ctx);
1192 bool returnNeedsCoercion =
1193 hasResult && fc.returnInfo.kind == ArgKind::Direct &&
1194 fc.returnInfo.coercedType && fc.returnInfo.coercedType != origRetTy;
1195 if (returnNeedsCoercion)
1196 callRetTy = fc.returnInfo.coercedType;
1198 builder.setInsertionPoint(call);
1199 auto newCall = cir::CallOp::create(builder, call.getLoc(),
1200 call.getCalleeAttr(), callRetTy, newArgs);
1201 for (mlir::NamedAttribute attr : call->getAttrs())
1202 if (!newCall->hasAttr(attr.getName()))
1203 newCall->setAttr(attr.getName(), attr.getValue());
1207 if (returnNeedsCoercion) {
1208 builder.setInsertionPointAfter(newCall);
1209 mlir::Value coercedBack =
1210 emitCoercion(builder, call.getLoc(), origRetTy, newCall.getResult(),
1212 call.getResult().replaceAllUsesWith(coercedBack);
1219 bool needsArgAttrUpdate =
1220 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1221 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1222 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1223 getFlattenedCoercedType(ac);
1225 if (needsArgAttrUpdate) {
1226 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1227 newCall->setAttr(
"arg_attrs",
1228 updateArgAttrs(ctx, origCallArgTypes, existing, fc));
1230 if (fc.returnInfo.kind == ArgKind::Extend) {
1231 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1232 newCall->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1235 if (hasResult && fc.returnInfo.kind == ArgKind::Ignore) {
1240 if (!call.getResult().use_empty()) {
1241 builder.setInsertionPointAfter(newCall);
1242 mlir::Value poison =
1243 createIgnoredValue(builder, call.getLoc(), origRetTy);
1244 call.getResult().replaceAllUsesWith(poison);
1246 }
else if (hasResult && !returnNeedsCoercion) {
1248 call.getResult().replaceAllUsesWith(newCall.getResult());
1258 SmallPtrSet<mlir::Operation *, 4> erased;
1259 for (cir::LoadOp wholeLoad : replacedWholeLoads)
1260 if (erased.insert(wholeLoad).second && wholeLoad.use_empty())
1263 return mlir::success();
mlir::LogicalResult rewriteFunctionDefinition(mlir::FunctionOpInterface funcOp, const mlir::abi::FunctionClassification &fc, mlir::OpBuilder &builder) override
mlir::LogicalResult rewriteCallSite(mlir::Operation *callOp, const mlir::abi::FunctionClassification &fc, mlir::OpBuilder &builder) override
C++ view class that accepts both !cir.struct and !cir.union types.
llvm::ArrayRef< mlir::Type > getMembers() const
size_t getNumElements() const
const internal::VariadicAllOfMatcher< Attr > attr