10#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
11#include "mlir/IR/Builders.h"
12#include "mlir/IR/Dominance.h"
19using namespace mlir::abi;
68 if (ac.kind != ArgKind::Direct || !ac.coercedType || !ac.canFlatten)
70 auto recTy = dyn_cast<cir::RecordType>(ac.coercedType);
71 if (!recTy || !recTy.isStruct() || recTy.getNumElements() <= 1)
83 const FunctionClassification &fc,
84 SmallVectorImpl<mlir::Type> &newArgTypes,
85 function_ref<mlir::InFlightDiagnostic()> emitError) {
86 assert(newArgTypes.empty() &&
"expected an empty output vector");
87 newArgTypes.reserve(oldArgTypes.size());
88 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
89 mlir::Type origTy = oldArgTypes[idx];
97 llvm::append_range(newArgTypes, flatTy.getMembers());
103 newArgTypes.push_back(ac.coercedType ? ac.coercedType : origTy);
106 case ArgKind::Ignore:
108 case ArgKind::Expand: {
112 auto recTy = cast<cir::RecordType>(origTy);
113 assert(recTy.isStruct() &&
114 "Expand classification requires a struct type, not a union");
115 assert(!recTy.getMembers().empty() &&
116 "Expand classification requires at least one struct field");
117 llvm::append_range(newArgTypes, recTy.getMembers());
120 case ArgKind::Extend:
127 newArgTypes.push_back(origTy);
129 case ArgKind::Indirect:
132 newArgTypes.push_back(cir::PointerType::get(origTy));
136 return mlir::success();
144computeNewReturnType(mlir::Type origRetTy,
const ArgClassification &retInfo,
145 mlir::MLIRContext *ctx,
146 function_ref<mlir::InFlightDiagnostic()> emitError) {
147 switch (retInfo.kind) {
148 case ArgKind::Direct:
151 return retInfo.coercedType ? retInfo.coercedType : origRetTy;
152 case ArgKind::Ignore:
153 return cir::VoidType::get(ctx);
154 case ArgKind::Expand:
155 emitError() <<
"Expand return is not allowed (classic codegen rejects "
156 <<
"it in EmitFunctionEpilog)";
158 case ArgKind::Extend:
163 case ArgKind::Indirect:
168 return cir::VoidType::get(ctx);
170 llvm_unreachable(
"all ArgKind cases handled");
178mlir::Value createIgnoredValue(mlir::OpBuilder &builder, mlir::Location loc,
180 return cir::ConstantOp::create(builder, loc, ty, cir::PoisonAttr::get(ty));
188mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx,
190 mlir::ArrayAttr existingArgAttrs,
191 const FunctionClassification &fc) {
192 mlir::Builder builder(ctx);
194 newArgAttrs.reserve(fc.argInfos.size());
195 for (
auto [oldIdx, ac] : llvm::enumerate(fc.argInfos)) {
196 if (ac.kind == ArgKind::Ignore)
198 mlir::DictionaryAttr existing = builder.getDictionaryAttr({});
199 if (existingArgAttrs && oldIdx < existingArgAttrs.size())
200 existing = mlir::cast<mlir::DictionaryAttr>(existingArgAttrs[oldIdx]);
204 newArgAttrs.append(flatTy.getNumElements(),
205 builder.getDictionaryAttr({}));
206 }
else if (ac.kind == ArgKind::Expand) {
209 auto recTy = cast<cir::RecordType>(origArgTypes[oldIdx]);
210 newArgAttrs.append(recTy.getNumElements(), builder.getDictionaryAttr({}));
211 }
else if (ac.kind == ArgKind::Extend) {
212 StringRef attrName = ac.signExtend
213 ? mlir::LLVM::LLVMDialect::getSExtAttrName()
214 :
mlir::LLVM::LLVMDialect::getZExtAttrName();
215 mlir::NamedAttrList attrs(existing);
216 attrs.set(attrName, builder.getUnitAttr());
217 newArgAttrs.push_back(attrs.getDictionary(ctx));
218 }
else if (ac.kind == ArgKind::Indirect) {
235 mlir::Type pointeeTy = origArgTypes[oldIdx];
236 StringRef ownershipAttr =
237 ac.byVal ? mlir::LLVM::LLVMDialect::getByValAttrName()
238 :
mlir::LLVM::LLVMDialect::getByRefAttrName();
239 mlir::NamedAttrList attrs(existing);
240 attrs.set(mlir::LLVM::LLVMDialect::getAlignAttrName(),
241 builder.getI64IntegerAttr(ac.indirectAlign.value()));
242 attrs.set(ownershipAttr, mlir::TypeAttr::get(pointeeTy));
244 attrs.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
245 builder.getUnitAttr());
246 attrs.set(mlir::LLVM::LLVMDialect::getNoUndefAttrName(),
247 builder.getUnitAttr());
249 newArgAttrs.push_back(attrs.getDictionary(ctx));
251 newArgAttrs.push_back(existing);
254 return builder.getArrayAttr(newArgAttrs);
260mlir::ArrayAttr updateResAttrs(mlir::MLIRContext *ctx,
261 mlir::ArrayAttr existingResAttrs,
262 const ArgClassification &retInfo) {
263 if (retInfo.kind != ArgKind::Extend)
264 return existingResAttrs;
267 if (existingResAttrs && !existingResAttrs.empty())
268 for (mlir::NamedAttribute na :
269 mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0]))
271 StringRef attrName = retInfo.signExtend ?
"llvm.signext" :
"llvm.zeroext";
272 attrs.push_back(mlir::NamedAttribute(mlir::StringAttr::get(ctx, attrName),
273 mlir::UnitAttr::get(ctx)));
274 return mlir::ArrayAttr::get(ctx, {mlir::DictionaryAttr::get(ctx, attrs)});
287static uint64_t coercionByteSize(mlir::Type ty,
const mlir::DataLayout &dl) {
288 if (
auto intTy = mlir::dyn_cast<cir::IntType>(ty))
289 return llvm::divideCeil(intTy.getWidth(), 8);
290 return dl.getTypeSize(ty);
318emitCoercionToMemory(mlir::OpBuilder &builder, mlir::Location loc,
319 mlir::Type dstTy, mlir::Value src, mlir::Block *slotBlock,
320 const mlir::DataLayout &dl,
321 SmallPtrSetImpl<mlir::Operation *> &createdOps) {
322 mlir::Type srcTy = src.getType();
323 assert(srcTy != dstTy &&
324 "emitCoercion callers must pre-check that the types differ");
326 uint64_t srcAlign = dl.getTypeABIAlignment(srcTy);
327 uint64_t dstAlign = dl.getTypeABIAlignment(dstTy);
328 uint64_t allocaAlign = std::max(srcAlign, dstAlign);
329 mlir::Type slotTy = coercionByteSize(srcTy, dl) >= coercionByteSize(dstTy, dl)
333 auto slotPtrTy = cir::PointerType::get(slotTy);
334 auto srcPtrTy = cir::PointerType::get(srcTy);
335 auto dstPtrTy = cir::PointerType::get(dstTy);
337 cir::AllocaOp alloca;
339 mlir::OpBuilder::InsertionGuard guard(builder);
340 builder.setInsertionPointToStart(slotBlock);
341 alloca = cir::AllocaOp::create(builder, loc, slotPtrTy,
342 builder.getStringAttr(
"coerce"),
343 builder.getI64IntegerAttr(allocaAlign));
345 createdOps.insert(alloca);
348 mlir::Value srcSlot = alloca;
349 if (slotTy != srcTy) {
350 auto srcCast = cir::CastOp::create(builder, loc, srcPtrTy,
351 cir::CastKind::bitcast, alloca);
352 createdOps.insert(srcCast);
355 auto store = cir::StoreOp::create(builder, loc, src, srcSlot);
356 createdOps.insert(store);
359 if (slotTy != dstTy) {
360 auto dstCast = cir::CastOp::create(builder, loc, dstPtrTy,
361 cir::CastKind::bitcast, alloca);
362 createdOps.insert(dstCast);
371mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
372 mlir::Type dstTy, mlir::Value src,
373 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
374 SmallPtrSetImpl<mlir::Operation *> &createdOps) {
375 mlir::Value dstSlot =
376 emitCoercionToMemory(builder, loc, dstTy, src, slotBlock, dl, createdOps);
377 auto load = cir::LoadOp::create(builder, loc, dstSlot);
378 createdOps.insert(load);
384mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
385 mlir::Type dstTy, mlir::Value src,
386 mlir::Block *slotBlock,
const mlir::DataLayout &dl) {
387 SmallPtrSet<mlir::Operation *, 4> ignored;
388 return emitCoercion(builder, loc, dstTy, src, slotBlock, dl, ignored);
400mlir::Block *coercionSlotBlock(mlir::Operation *op) {
401 if (
auto funcOp = op->getParentOfType<mlir::FunctionOpInterface>())
402 return &funcOp->getRegion(0).front();
403 mlir::Region *region = op->getParentRegion();
404 while (mlir::Region *outer = region->getParentRegion()) {
405 if (mlir::isa<mlir::ModuleOp>(outer->getParentOp()))
409 assert(!region->empty() &&
"coercion slot needs a block to hold the alloca");
410 return ®ion->front();
415void insertReturnCoercion(mlir::FunctionOpInterface funcOp,
416 mlir::Type origRetTy, mlir::Type coercedRetTy,
417 mlir::OpBuilder &builder,
418 const mlir::DataLayout &dl) {
420 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
421 for (cir::ReturnOp r : returns) {
422 if (r.getInput().empty())
424 mlir::Value origVal = r.getInput()[0];
425 if (origVal.getType() == coercedRetTy)
427 builder.setInsertionPoint(r);
428 mlir::Value coerced =
429 emitCoercion(builder, r.getLoc(), coercedRetTy, origVal,
430 &funcOp->getRegion(0).front(), dl);
431 r->setOperand(0, coerced);
439static std::pair<cir::AllocaOp, cir::LoadOp>
440getWholeRecordSource(mlir::Value recordVal) {
441 cir::LoadOp load = recordVal.getDefiningOp<cir::LoadOp>();
442 if (!load || load.getIsVolatile() || load.getMemOrder())
447 auto alloca = load.getAddr().getDefiningOp<cir::AllocaOp>();
450 return {alloca, load};
464static void emitStructFieldArgs(mlir::OpBuilder &builder, mlir::Location loc,
466 SmallVectorImpl<mlir::Value> &newArgs,
467 SmallVectorImpl<cir::LoadOp> &deadRecordLoads) {
468 auto [srcAlloca, srcLoad] = getWholeRecordSource(structVal);
471 mlir::OpBuilder::InsertionGuard guard(builder);
472 builder.setInsertionPoint(srcLoad);
473 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
474 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
475 mlir::Value fieldPtr = cir::GetMemberOp::create(
476 builder, loc, fieldPtrTy, srcAlloca,
"", f);
477 newArgs.push_back(cir::LoadOp::create(builder, loc, fieldPtr));
479 deadRecordLoads.push_back(srcLoad);
483 cir::ExtractMemberOp::create(builder, loc, structVal, f));
494 for (mlir::Operation *load : uniqueLoads)
495 if (load->use_empty())
516void insertArgCoercion(mlir::FunctionOpInterface funcOp,
517 const FunctionClassification &fc,
518 mlir::OpBuilder &builder,
const mlir::DataLayout &dl,
520 mlir::Region &body = funcOp->getRegion(0);
523 mlir::Block &entry = body.front();
529 unsigned blockArgIdx = hasSRetArg ? 1 : 0;
531 for (
const ArgClassification &ac : fc.argInfos) {
532 assert(blockArgIdx < entry.getNumArguments() &&
533 "classification count must not exceed entry block arguments");
535 if (ac.kind == ArgKind::Expand) {
539 mlir::BlockArgument origArg = entry.getArgument(blockArgIdx);
540 auto recTy = cast<cir::RecordType>(origArg.getType());
542 "Expand classification requires a struct type, not a union");
544 assert(numFields > 0 &&
545 "Expand classification requires at least one struct field");
546 mlir::Location loc = funcOp.getLoc();
556 cir::StoreOp paramStore;
557 cir::AllocaOp destAlloca;
558 if (!origArg.use_empty()) {
559 assert(origArg.hasOneUse() &&
560 "Expand arg must have exactly one use (the CIRGen param spill)");
561 paramStore = cast<cir::StoreOp>(*origArg.user_begin());
562 assert(paramStore.getValue() == origArg &&
563 "Expand arg's use must be the value operand of its store");
564 destAlloca = cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
571 mlir::Operation *fieldStoreInsertPt =
nullptr;
573 fieldStoreInsertPt = paramStore->getNextNode();
574 assert(fieldStoreInsertPt &&
575 "param spill must be followed by a block terminator");
587 builder.setInsertionPoint(fieldStoreInsertPt);
588 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
590 origArg.setType(fieldTy);
592 entry.insertArgument(blockArgIdx + f, fieldTy, loc);
595 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
596 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
599 cir::StoreOp::create(builder, loc, entry.getArgument(blockArgIdx + f),
603 blockArgIdx += numFields;
607 mlir::BlockArgument blockArg = entry.getArgument(blockArgIdx);
616 unsigned numFields = flatTy.getNumElements();
617 assert(numFields >= 2 &&
"getFlattenedCoercedType guarantees >1 fields");
618 Type origTy = blockArg.getType();
619 Location loc = funcOp.getLoc();
622 blockArg.setType(flatTy.getElementType(0));
623 for (
unsigned f = 1; f < numFields; ++f)
624 entry.insertArgument(blockArgIdx + f, flatTy.getElementType(f), loc);
627 builder.setInsertionPointToStart(&entry);
628 auto flatPtrTy = cir::PointerType::get(flatTy);
629 uint64_t flatAlign = dl.getTypeABIAlignment(flatTy);
630 auto flatSlot = cir::AllocaOp::create(
631 builder, loc, flatPtrTy, builder.getStringAttr(
"coerce"),
632 builder.getI64IntegerAttr(flatAlign));
633 SmallPtrSet<Operation *, 8> flattenOps = {flatSlot};
634 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
635 Type fieldPtrTy = cir::PointerType::get(fieldTy);
636 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
639 flattenOps.insert(fieldPtr);
640 auto storeOp = cir::StoreOp::create(
641 builder, loc, entry.getArgument(blockArgIdx + f), fieldPtr);
642 flattenOps.insert(storeOp);
645 cir::LoadOp::create(builder, loc, flatTy, flatSlot.getResult());
646 flattenOps.insert(flatLoaded);
650 Value finalVal = flatLoaded;
651 if (origTy != flatTy) {
652 SmallPtrSet<Operation *, 4> coercionOps;
653 finalVal = emitCoercion(builder, loc, origTy, flatLoaded, &entry, dl,
655 flattenOps.insert(coercionOps.begin(), coercionOps.end());
660 blockArg.replaceAllUsesExcept(finalVal, flattenOps);
662 blockArgIdx += numFields;
666 if (ac.kind == ArgKind::Direct && ac.coercedType) {
667 mlir::Type oldArgTy = blockArg.getType();
668 mlir::Type newArgTy = ac.coercedType;
669 if (oldArgTy == newArgTy) {
673 blockArg.setType(newArgTy);
675 builder.setInsertionPointToStart(&entry);
676 SmallPtrSet<mlir::Operation *, 4> coercionOps;
677 mlir::Value adapted = emitCoercion(builder, funcOp.getLoc(), oldArgTy,
678 blockArg, &entry, dl, coercionOps);
684 blockArg.replaceAllUsesExcept(adapted, coercionOps);
685 }
else if (ac.kind == ArgKind::Indirect) {
690 auto ptrTy = cir::PointerType::get(blockArg.getType());
703 cir::StoreOp paramStore;
704 cir::AllocaOp destAlloca;
705 if (!blockArg.use_empty()) {
706 assert(blockArg.hasOneUse() &&
707 "byref arg must have exactly one use (the CIRGen param "
709 paramStore = cast<cir::StoreOp>(*blockArg.user_begin());
710 assert(paramStore.getValue() == blockArg &&
711 "byref arg's use must be the value operand of its store");
713 cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
720 blockArg.setType(ptrTy);
723 destAlloca.getResult().replaceAllUsesWith(blockArg);
729 blockArg.setType(ptrTy);
731 builder.setInsertionPointToStart(&entry);
732 auto loadOp = cir::LoadOp::create(builder, funcOp.getLoc(), blockArg);
733 SmallPtrSet<mlir::Operation *, 1> loadOps = {loadOp};
734 blockArg.replaceAllUsesExcept(loadOp.getResult(), loadOps);
771void insertSRetStores(mlir::FunctionOpInterface funcOp, mlir::Type origRetTy,
772 mlir::OpBuilder &builder) {
773 mlir::Value sretPtr = funcOp.getArguments()[0];
776 funcOp->walk([&](cir::ReturnOp retOp) { returnOps.push_back(retOp); });
778 cir::AllocaOp retAlloca =
nullptr;
779 for (cir::ReturnOp retOp : returnOps) {
782 assert(!retOp.getInput().empty() &&
783 "cir.return in sret function must have an operand");
785 cir::LoadOp retLoad =
786 mlir::cast<cir::LoadOp>(retOp.getInput()[0].getDefiningOp());
794 retAlloca = mlir::cast<cir::AllocaOp>(retLoad.getAddr().getDefiningOp());
795 retAlloca.getResult().replaceAllUsesWith(sretPtr);
801 builder.setInsertionPoint(retOp);
802 cir::ReturnOp::create(builder, retOp.getLoc());
804 if (retLoad.use_empty())
823 builder.getNamedAttr(
"llvm.sret", mlir::TypeAttr::get(retTy)));
825 builder.getNamedAttr(
"llvm.align", builder.getI64IntegerAttr(align)));
828 builder.getNamedAttr(
"llvm.noalias", builder.getUnitAttr()));
829 attrs.push_back(builder.getNamedAttr(
"llvm.writable", builder.getUnitAttr()));
831 builder.getNamedAttr(
"llvm.dead_on_unwind", builder.getUnitAttr()));
841void applySretSlotAttrs(cir::CallOp newCall, mlir::ArrayAttr argAttrs,
842 mlir::Type retTy, uint64_t align,
843 mlir::OpBuilder &builder) {
844 mlir::MLIRContext *ctx = newCall->getContext();
846 buildSretSlotAttrs(builder, retTy, align,
false);
849 newArgAttrs.reserve(newCall.getArgOperands().size());
850 newArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
852 llvm::append_range(newArgAttrs, argAttrs);
853 assert(newArgAttrs.size() <= newCall.getArgOperands().size() &&
854 "arg_attrs wider than the rewritten call's operand list");
855 newArgAttrs.resize(newCall.getArgOperands().size(),
856 mlir::DictionaryAttr::get(ctx));
857 newCall->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, newArgAttrs));
864static void prependIndirectCallee(cir::CallOp call,
865 SmallVectorImpl<mlir::Value> &args,
866 mlir::Type retTy, mlir::OpBuilder &builder) {
867 if (!call.isIndirect())
869 mlir::Value calleePtr = call.getIndirectCall();
871 paramTypes.reserve(args.size());
872 llvm::transform(args, std::back_inserter(paramTypes),
873 [](mlir::Value v) {
return v.getType(); });
882 auto calleeFnTy = cast<cir::FuncType>(
883 cast<cir::PointerType>(calleePtr.getType()).getPointee());
884 auto newPtrTy = cir::PointerType::get(
885 cir::FuncType::get(paramTypes, retTy, calleeFnTy.isVarArg()));
886 if (calleePtr.getType() != newPtrTy)
887 calleePtr = cir::CastOp::create(builder, call.getLoc(), newPtrTy,
888 cir::CastKind::bitcast, calleePtr);
889 args.insert(args.begin(), calleePtr);
899void rewriteIndirectReturnCall(cir::CallOp call,
900 const FunctionClassification &fc,
902 mlir::Type origRetTy,
904 mlir::OpBuilder &builder) {
905 mlir::MLIRContext *ctx = call->getContext();
906 auto ptrTy = cir::PointerType::get(origRetTy);
907 builder.setInsertionPoint(call);
908 uint64_t sretAlign = fc.returnInfo.indirectAlign.value();
921 mlir::Value sretSlot =
nullptr;
922 cir::StoreOp reuseStore =
nullptr;
923 if (call.getResult().hasOneUse()) {
924 mlir::Operation *user = *call.getResult().getUsers().begin();
925 if (
auto store = mlir::dyn_cast<cir::StoreOp>(user))
926 if (store.getValue() == call.getResult() &&
927 store.getAddr().getType() == ptrTy &&
928 mlir::DominanceInfo().properlyDominates(store.getAddr(), call)) {
929 sretSlot = store.getAddr();
934 auto alloca = cir::AllocaOp::create(
935 builder, call.getLoc(), ptrTy,
936 builder.getStringAttr(
"sret"),
937 builder.getI64IntegerAttr(sretAlign));
942 sretArgs.push_back(sretSlot);
943 sretArgs.append(newArgs.begin(), newArgs.end());
945 mlir::Type sretVoidTy = cir::VoidType::get(ctx);
946 prependIndirectCallee(call, sretArgs, sretVoidTy, builder);
947 auto newCall = cir::CallOp::create(
948 builder, call.getLoc(), call.getCalleeAttr(), sretVoidTy, sretArgs);
949 for (mlir::NamedAttribute attr : call->getAttrs())
950 if (!newCall->hasAttr(
attr.getName()))
951 newCall->setAttr(
attr.getName(),
attr.getValue());
958 mlir::ArrayAttr argAttrs = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
959 bool needsArgAttrUpdate =
960 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
961 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
962 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
963 getFlattenedCoercedType(ac);
965 if (needsArgAttrUpdate)
966 argAttrs = updateArgAttrs(ctx, origCallArgTypes, argAttrs, fc);
967 applySretSlotAttrs(newCall, argAttrs, origRetTy, sretAlign, builder);
975 builder.setInsertionPointAfter(newCall);
976 auto load = cir::LoadOp::create(builder, call.getLoc(), origRetTy, sretSlot,
981 cir::SyncScopeKindAttr(),
984 call.getResult().replaceAllUsesWith(load);
992 mlir::FunctionOpInterface funcOpInterface,
const FunctionClassification &fc,
993 mlir::OpBuilder &builder) {
999 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(funcOpInterface);
1001 if (!fc.needsRewrite())
1002 return mlir::success();
1006 mlir::MLIRContext *ctx = funcOp->getContext();
1011 assert(oldResultTypes.size() <= 1 &&
1012 "CIR functions return zero or one value");
1015 if (mlir::failed(buildNewArgTypes(oldArgTypes, fc, newArgTypes,
1016 [&]() {
return funcOp.emitOpError(); })))
1017 return mlir::failure();
1019 mlir::Type voidTy = cir::VoidType::get(ctx);
1020 mlir::Type origRetTy = oldResultTypes.empty() ? voidTy : oldResultTypes[0];
1021 mlir::Type newRetTy = computeNewReturnType(
1022 origRetTy, fc.returnInfo, ctx, [&]() { return funcOp.emitOpError(); });
1024 return mlir::failure();
1034 fc.returnInfo.kind == ArgKind::Indirect && !oldResultTypes.empty();
1036 newArgTypes.insert(newArgTypes.begin(), cir::PointerType::get(origRetTy));
1038 if (funcOp.isDefinition()) {
1039 mlir::Region &body = funcOp->getRegion(0);
1040 if (!body.empty()) {
1045 body.front().insertArgument(0u, cir::PointerType::get(origRetTy),
1047 insertSRetStores(funcOp, origRetTy, builder);
1057 insertArgCoercion(funcOp, fc, builder, dl, hasSRet);
1062 if (fc.returnInfo.kind == ArgKind::Direct && fc.returnInfo.coercedType &&
1063 !oldResultTypes.empty() && fc.returnInfo.coercedType != origRetTy)
1064 insertReturnCoercion(funcOp, origRetTy, fc.returnInfo.coercedType,
1067 mlir::Block &entry = body.front();
1076 unsigned blockArgIdx = hasSRet ? 1 : 0;
1077 for (
auto [i, ac] : llvm::enumerate(fc.argInfos)) {
1078 if (blockArgIdx >= entry.getNumArguments())
1080 if (ac.kind == ArgKind::Ignore) {
1081 mlir::BlockArgument arg = entry.getArgument(blockArgIdx);
1082 if (!arg.use_empty()) {
1083 builder.setInsertionPointToStart(&entry);
1084 mlir::Value poison =
1085 createIgnoredValue(builder, funcOp.getLoc(), arg.getType());
1086 arg.replaceAllUsesWith(poison);
1088 entry.eraseArgument(blockArgIdx);
1092 blockArgIdx += flatTy.getNumElements();
1093 else if (ac.kind == ArgKind::Expand)
1094 blockArgIdx += cast<cir::RecordType>(oldArgTypes[i]).getNumElements();
1106 if (fc.returnInfo.kind == ArgKind::Ignore && !oldResultTypes.empty()) {
1107 assert(mlir::isa<cir::VoidType>(newRetTy) &&
1108 "Ignore-return path requires the new return type to be void");
1110 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
1111 for (cir::ReturnOp r : returns) {
1112 if (r.getNumOperands() == 0)
1114 builder.setInsertionPoint(r);
1115 cir::ReturnOp::create(builder, r.getLoc());
1121 mlir::Type newFnTy = funcOp.cloneTypeWith(newArgTypes, newResultTypes);
1122 funcOp.setFunctionTypeAttr(mlir::TypeAttr::get(newFnTy));
1129 bool needsArgAttrUpdate =
1130 hasSRet || llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1131 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1132 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1133 getFlattenedCoercedType(ac);
1135 if (needsArgAttrUpdate) {
1136 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1137 mlir::ArrayAttr updated = updateArgAttrs(ctx, oldArgTypes, existing, fc);
1143 builder, origRetTy, fc.returnInfo.indirectAlign.value(),
1144 funcOp.isDefinition());
1146 withSret.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1147 llvm::append_range(withSret, updated);
1148 funcOp->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, withSret));
1150 funcOp->setAttr(
"arg_attrs", updated);
1156 if (fc.returnInfo.kind == ArgKind::Extend) {
1157 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1158 funcOp->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1161 return mlir::success();
1166 const FunctionClassification &fc,
1167 mlir::OpBuilder &builder) {
1177 unsigned numOperands =
1178 mlir::cast<cir::CIRCallOpInterface>(callOp).getNumArgOperands();
1179 if (numOperands > fc.argInfos.size())
1180 return callOp->emitOpError()
1181 <<
"variadic arguments not yet implemented in CallConvLowering";
1182 if (numOperands < fc.argInfos.size())
1183 return callOp->emitOpError()
1184 <<
"call passes fewer arguments than the callee declares, which is "
1185 "not yet implemented in CallConvLowering";
1187 if (!fc.needsRewrite())
1188 return mlir::success();
1190 if (mlir::isa<cir::TryCallOp>(callOp))
1191 return callOp->emitOpError()
1192 <<
"TryCallOp not yet implemented in CallConvLowering";
1194 auto call = mlir::cast<cir::CallOp>(callOp);
1195 mlir::MLIRContext *ctx = callOp->getContext();
1196 mlir::Block *slotBlock = coercionSlotBlock(call);
1198 builder.setInsertionPoint(call);
1201 mlir::ValueRange argOperands = call.getArgOperands();
1202 newArgs.reserve(argOperands.size());
1213 llvm::append_range(origCallArgTypes, argOperands.getTypes());
1214 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1215 if (ac.kind == ArgKind::Ignore)
1217 mlir::Value arg = argOperands[idx];
1225 if (arg.getType() != flatTy) {
1226 SmallPtrSet<mlir::Operation *, 4> coercionOps;
1227 mlir::Value coercedPtr = emitCoercionToMemory(
1228 builder, call.getLoc(), flatTy, arg, slotBlock, dl, coercionOps);
1229 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
1230 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
1232 cir::GetMemberOp::create(builder, call.getLoc(), fieldPtrTy,
1234 newArgs.push_back(cir::LoadOp::create(builder, call.getLoc(), fieldTy,
1235 fieldPtr.getResult()));
1238 emitStructFieldArgs(builder, call.getLoc(), arg, flatTy, newArgs,
1241 }
else if (ac.kind == ArgKind::Expand) {
1244 auto recTy = cast<cir::RecordType>(arg.getType());
1246 "Expand classification requires a struct type, not a union");
1247 emitStructFieldArgs(builder, call.getLoc(), arg, recTy, newArgs,
1249 }
else if (ac.kind == ArgKind::Direct && ac.coercedType &&
1250 arg.getType() != ac.coercedType) {
1251 arg = emitCoercion(builder, call.getLoc(), ac.coercedType, arg, slotBlock,
1253 newArgs.push_back(arg);
1254 }
else if (ac.kind == ArgKind::Indirect) {
1261 auto [srcAlloca, srcLoad] = getWholeRecordSource(arg);
1263 return call->emitOpError()
1264 <<
"byref argument that is not a load of an alloca is not yet "
1265 "implemented in CallConvLowering";
1266 assert(srcAlloca.getAlignment() >= ac.indirectAlign.value() &&
1267 "llvm.align on a byref argument must not overstate the "
1269 newArgs.push_back(srcAlloca);
1270 deadRecordLoads.push_back(srcLoad);
1273 auto ptrTy = cir::PointerType::get(arg.getType());
1274 auto slot = cir::AllocaOp::create(
1275 builder, call.getLoc(), ptrTy, builder.getStringAttr(
"byval"),
1276 builder.getI64IntegerAttr(ac.indirectAlign.value()));
1277 cir::StoreOp::create(builder, call.getLoc(), arg, slot);
1278 newArgs.push_back(slot);
1280 newArgs.push_back(arg);
1284 bool hasResult = call.getNumResults() > 0;
1285 mlir::Type origRetTy =
1286 hasResult ? call.getResult().getType() : cir::VoidType::get(ctx);
1292 if (fc.returnInfo.kind == ArgKind::Indirect && hasResult) {
1293 rewriteIndirectReturnCall(call, fc, newArgs, origRetTy, origCallArgTypes,
1295 eraseDeadRecordLoads(deadRecordLoads);
1296 return mlir::success();
1299 mlir::Type callRetTy = origRetTy;
1300 if (fc.returnInfo.kind == ArgKind::Ignore && hasResult)
1301 callRetTy = cir::VoidType::get(ctx);
1302 bool returnNeedsCoercion =
1303 hasResult && fc.returnInfo.kind == ArgKind::Direct &&
1304 fc.returnInfo.coercedType && fc.returnInfo.coercedType != origRetTy;
1305 if (returnNeedsCoercion)
1306 callRetTy = fc.returnInfo.coercedType;
1308 builder.setInsertionPoint(call);
1309 prependIndirectCallee(call, newArgs, callRetTy, builder);
1310 auto newCall = cir::CallOp::create(builder, call.getLoc(),
1311 call.getCalleeAttr(), callRetTy, newArgs);
1312 for (mlir::NamedAttribute attr : call->getAttrs())
1313 if (!newCall->hasAttr(attr.getName()))
1314 newCall->setAttr(attr.getName(), attr.getValue());
1318 if (returnNeedsCoercion) {
1319 builder.setInsertionPointAfter(newCall);
1320 mlir::Value coercedBack = emitCoercion(builder, call.getLoc(), origRetTy,
1321 newCall.getResult(), slotBlock, dl);
1322 call.getResult().replaceAllUsesWith(coercedBack);
1329 bool needsArgAttrUpdate =
1330 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1331 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1332 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1333 getFlattenedCoercedType(ac);
1335 if (needsArgAttrUpdate) {
1336 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1337 newCall->setAttr(
"arg_attrs",
1338 updateArgAttrs(ctx, origCallArgTypes, existing, fc));
1340 if (fc.returnInfo.kind == ArgKind::Extend) {
1341 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1342 newCall->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1345 if (hasResult && fc.returnInfo.kind == ArgKind::Ignore) {
1350 if (!call.getResult().use_empty()) {
1351 builder.setInsertionPointAfter(newCall);
1352 mlir::Value poison =
1353 createIgnoredValue(builder, call.getLoc(), origRetTy);
1354 call.getResult().replaceAllUsesWith(poison);
1356 }
else if (hasResult && !returnNeedsCoercion) {
1358 call.getResult().replaceAllUsesWith(newCall.getResult());
1362 eraseDeadRecordLoads(deadRecordLoads);
1364 return mlir::success();
1369 mlir::OpBuilder &builder) {
1370 auto oldPtrTy = mlir::cast<cir::PointerType>(addrOp.getAddr().getType());
1371 cir::FuncType newFuncTy = funcOp.getFunctionType();
1374 if (newFuncTy == oldPtrTy.getPointee())
1378 addrOp.getAddr().setType(cir::PointerType::get(newFuncTy));
1379 if (addrOp.getAddr().use_empty())
1386 mlir::OpBuilder::InsertionGuard guard(builder);
1387 builder.setInsertionPointAfter(addrOp);
1388 auto bitcast = cir::CastOp::create(builder, addrOp.getLoc(), oldPtrTy,
1389 cir::CastKind::bitcast, addrOp.getAddr());
1390 addrOp.getAddr().replaceAllUsesExcept(bitcast.getResult(), bitcast);
void rewriteFunctionAddress(cir::GetGlobalOp addrOp, cir::FuncOp funcOp, mlir::OpBuilder &builder)
Retype addrOp, which holds the address of funcOp, to the signature funcOp was rewritten to,...
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