10#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
11#include "mlir/IR/Builders.h"
12#include "mlir/IR/Dominance.h"
18using namespace mlir::abi;
60 if (ac.kind != ArgKind::Direct || !ac.coercedType || !ac.canFlatten)
62 auto recTy = dyn_cast<cir::RecordType>(ac.coercedType);
63 if (!recTy || !recTy.isStruct() || recTy.getNumElements() <= 1)
75 const FunctionClassification &fc,
76 SmallVectorImpl<mlir::Type> &newArgTypes,
77 function_ref<mlir::InFlightDiagnostic()> emitError) {
78 assert(newArgTypes.empty() &&
"expected an empty output vector");
79 newArgTypes.reserve(oldArgTypes.size());
80 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
81 mlir::Type origTy = oldArgTypes[idx];
89 llvm::append_range(newArgTypes, flatTy.getMembers());
95 newArgTypes.push_back(ac.coercedType ? ac.coercedType : origTy);
100 case ArgKind::Expand: {
104 auto recTy = cast<cir::RecordType>(origTy);
105 assert(recTy.isStruct() &&
106 "Expand classification requires a struct type, not a union");
107 assert(!recTy.getMembers().empty() &&
108 "Expand classification requires at least one struct field");
109 llvm::append_range(newArgTypes, recTy.getMembers());
112 case ArgKind::Extend:
119 newArgTypes.push_back(origTy);
121 case ArgKind::Indirect:
127 newArgTypes.push_back(cir::PointerType::get(origTy));
131 return mlir::success();
139computeNewReturnType(mlir::Type origRetTy,
const ArgClassification &retInfo,
140 mlir::MLIRContext *ctx,
141 function_ref<mlir::InFlightDiagnostic()> emitError) {
142 switch (retInfo.kind) {
143 case ArgKind::Direct:
146 return retInfo.coercedType ? retInfo.coercedType : origRetTy;
147 case ArgKind::Ignore:
148 return cir::VoidType::get(ctx);
149 case ArgKind::Expand:
150 emitError() <<
"Expand return is not allowed (classic codegen rejects "
151 <<
"it in EmitFunctionEpilog)";
153 case ArgKind::Extend:
158 case ArgKind::Indirect:
163 return cir::VoidType::get(ctx);
165 llvm_unreachable(
"all ArgKind cases handled");
173mlir::Value createIgnoredValue(mlir::OpBuilder &builder, mlir::Location loc,
175 return cir::ConstantOp::create(builder, loc, ty, cir::PoisonAttr::get(ty));
183mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx,
185 mlir::ArrayAttr existingArgAttrs,
186 const FunctionClassification &fc) {
187 mlir::Builder builder(ctx);
189 newArgAttrs.reserve(fc.argInfos.size());
190 for (
auto [oldIdx, ac] : llvm::enumerate(fc.argInfos)) {
191 if (ac.kind == ArgKind::Ignore)
193 mlir::DictionaryAttr existing = builder.getDictionaryAttr({});
194 if (existingArgAttrs && oldIdx < existingArgAttrs.size())
195 existing = mlir::cast<mlir::DictionaryAttr>(existingArgAttrs[oldIdx]);
199 newArgAttrs.append(flatTy.getNumElements(),
200 builder.getDictionaryAttr({}));
201 }
else if (ac.kind == ArgKind::Expand) {
204 auto recTy = cast<cir::RecordType>(origArgTypes[oldIdx]);
205 newArgAttrs.append(recTy.getNumElements(), builder.getDictionaryAttr({}));
206 }
else if (ac.kind == ArgKind::Extend) {
207 StringRef attrName = ac.signExtend
208 ? mlir::LLVM::LLVMDialect::getSExtAttrName()
209 :
mlir::LLVM::LLVMDialect::getZExtAttrName();
210 mlir::NamedAttrList attrs(existing);
211 attrs.set(attrName, builder.getUnitAttr());
212 newArgAttrs.push_back(attrs.getDictionary(ctx));
213 }
else if (ac.kind == ArgKind::Indirect) {
230 mlir::Type pointeeTy = origArgTypes[oldIdx];
231 StringRef ownershipAttr =
232 ac.byVal ? mlir::LLVM::LLVMDialect::getByValAttrName()
233 :
mlir::LLVM::LLVMDialect::getByRefAttrName();
234 mlir::NamedAttrList attrs(existing);
235 attrs.set(mlir::LLVM::LLVMDialect::getAlignAttrName(),
236 builder.getI64IntegerAttr(ac.indirectAlign.value()));
237 attrs.set(ownershipAttr, mlir::TypeAttr::get(pointeeTy));
239 attrs.set(mlir::LLVM::LLVMDialect::getNoAliasAttrName(),
240 builder.getUnitAttr());
241 attrs.set(mlir::LLVM::LLVMDialect::getNoUndefAttrName(),
242 builder.getUnitAttr());
244 newArgAttrs.push_back(attrs.getDictionary(ctx));
246 newArgAttrs.push_back(existing);
249 return builder.getArrayAttr(newArgAttrs);
255mlir::ArrayAttr updateResAttrs(mlir::MLIRContext *ctx,
256 mlir::ArrayAttr existingResAttrs,
257 const ArgClassification &retInfo) {
258 if (retInfo.kind != ArgKind::Extend)
259 return existingResAttrs;
262 if (existingResAttrs && !existingResAttrs.empty())
263 for (mlir::NamedAttribute na :
264 mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0]))
266 StringRef attrName = retInfo.signExtend ?
"llvm.signext" :
"llvm.zeroext";
267 attrs.push_back(mlir::NamedAttribute(mlir::StringAttr::get(ctx, attrName),
268 mlir::UnitAttr::get(ctx)));
269 return mlir::ArrayAttr::get(ctx, {mlir::DictionaryAttr::get(ctx, attrs)});
297emitCoercionToMemory(mlir::OpBuilder &builder, mlir::Location loc,
298 mlir::Type dstTy, mlir::Value src, mlir::Block *slotBlock,
299 const mlir::DataLayout &dl,
300 SmallPtrSetImpl<mlir::Operation *> &createdOps) {
301 mlir::Type srcTy = src.getType();
302 assert(srcTy != dstTy &&
303 "emitCoercion callers must pre-check that the types differ");
305 uint64_t srcAlign = dl.getTypeABIAlignment(srcTy);
306 uint64_t dstAlign = dl.getTypeABIAlignment(dstTy);
307 uint64_t allocaAlign = std::max(srcAlign, dstAlign);
309 dl.getTypeSize(srcTy) >= dl.getTypeSize(dstTy) ? srcTy : dstTy;
311 auto slotPtrTy = cir::PointerType::get(slotTy);
312 auto srcPtrTy = cir::PointerType::get(srcTy);
313 auto dstPtrTy = cir::PointerType::get(dstTy);
315 cir::AllocaOp alloca;
317 mlir::OpBuilder::InsertionGuard guard(builder);
318 builder.setInsertionPointToStart(slotBlock);
319 alloca = cir::AllocaOp::create(builder, loc, slotPtrTy,
320 builder.getStringAttr(
"coerce"),
321 builder.getI64IntegerAttr(allocaAlign));
323 createdOps.insert(alloca);
326 mlir::Value srcSlot = alloca;
327 if (slotTy != srcTy) {
328 auto srcCast = cir::CastOp::create(builder, loc, srcPtrTy,
329 cir::CastKind::bitcast, alloca);
330 createdOps.insert(srcCast);
333 auto store = cir::StoreOp::create(builder, loc, src, srcSlot);
334 createdOps.insert(store);
337 if (slotTy != dstTy) {
338 auto dstCast = cir::CastOp::create(builder, loc, dstPtrTy,
339 cir::CastKind::bitcast, alloca);
340 createdOps.insert(dstCast);
349mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
350 mlir::Type dstTy, mlir::Value src,
351 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
352 SmallPtrSetImpl<mlir::Operation *> &createdOps) {
353 mlir::Value dstSlot =
354 emitCoercionToMemory(builder, loc, dstTy, src, slotBlock, dl, createdOps);
355 auto load = cir::LoadOp::create(builder, loc, dstSlot);
356 createdOps.insert(load);
362mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
363 mlir::Type dstTy, mlir::Value src,
364 mlir::Block *slotBlock,
const mlir::DataLayout &dl) {
365 SmallPtrSet<mlir::Operation *, 4> ignored;
366 return emitCoercion(builder, loc, dstTy, src, slotBlock, dl, ignored);
378mlir::Block *coercionSlotBlock(mlir::Operation *op) {
379 if (
auto funcOp = op->getParentOfType<mlir::FunctionOpInterface>())
380 return &funcOp->getRegion(0).front();
381 mlir::Region *region = op->getParentRegion();
382 while (mlir::Region *outer = region->getParentRegion()) {
383 if (mlir::isa<mlir::ModuleOp>(outer->getParentOp()))
387 assert(!region->empty() &&
"coercion slot needs a block to hold the alloca");
388 return ®ion->front();
393void insertReturnCoercion(mlir::FunctionOpInterface funcOp,
394 mlir::Type origRetTy, mlir::Type coercedRetTy,
395 mlir::OpBuilder &builder,
396 const mlir::DataLayout &dl) {
398 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
399 for (cir::ReturnOp r : returns) {
400 if (r.getInput().empty())
402 mlir::Value origVal = r.getInput()[0];
403 if (origVal.getType() == coercedRetTy)
405 builder.setInsertionPoint(r);
406 mlir::Value coerced =
407 emitCoercion(builder, r.getLoc(), coercedRetTy, origVal,
408 &funcOp->getRegion(0).front(), dl);
409 r->setOperand(0, coerced);
425emitStructFieldArgs(mlir::OpBuilder &builder, mlir::Location loc,
427 SmallVectorImpl<mlir::Value> &newArgs,
428 SmallVectorImpl<cir::LoadOp> &replacedWholeLoads) {
429 cir::LoadOp wholeLoad = structVal.getDefiningOp<cir::LoadOp>();
430 cir::AllocaOp srcAlloca;
431 if (wholeLoad && !wholeLoad.getIsVolatile() && !wholeLoad.getMemOrder())
432 srcAlloca = wholeLoad.getAddr().getDefiningOp<cir::AllocaOp>();
435 mlir::OpBuilder::InsertionGuard guard(builder);
436 builder.setInsertionPoint(wholeLoad);
437 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
438 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
439 mlir::Value fieldPtr = cir::GetMemberOp::create(
440 builder, loc, fieldPtrTy, srcAlloca,
"", f);
441 newArgs.push_back(cir::LoadOp::create(builder, loc, fieldPtr));
443 replacedWholeLoads.push_back(wholeLoad);
447 cir::ExtractMemberOp::create(builder, loc, structVal, f));
468void insertArgCoercion(mlir::FunctionOpInterface funcOp,
469 const FunctionClassification &fc,
470 mlir::OpBuilder &builder,
const mlir::DataLayout &dl,
472 mlir::Region &body = funcOp->getRegion(0);
475 mlir::Block &entry = body.front();
481 unsigned blockArgIdx = hasSRetArg ? 1 : 0;
483 for (
const ArgClassification &ac : fc.argInfos) {
484 assert(blockArgIdx < entry.getNumArguments() &&
485 "classification count must not exceed entry block arguments");
487 if (ac.kind == ArgKind::Expand) {
491 mlir::BlockArgument origArg = entry.getArgument(blockArgIdx);
492 auto recTy = cast<cir::RecordType>(origArg.getType());
494 "Expand classification requires a struct type, not a union");
496 assert(numFields > 0 &&
497 "Expand classification requires at least one struct field");
498 mlir::Location loc = funcOp.getLoc();
508 cir::StoreOp paramStore;
509 cir::AllocaOp destAlloca;
510 if (!origArg.use_empty()) {
511 assert(origArg.hasOneUse() &&
512 "Expand arg must have exactly one use (the CIRGen param spill)");
513 paramStore = cast<cir::StoreOp>(*origArg.user_begin());
514 assert(paramStore.getValue() == origArg &&
515 "Expand arg's use must be the value operand of its store");
516 destAlloca = cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
523 mlir::Operation *fieldStoreInsertPt =
nullptr;
525 fieldStoreInsertPt = paramStore->getNextNode();
526 assert(fieldStoreInsertPt &&
527 "param spill must be followed by a block terminator");
539 builder.setInsertionPoint(fieldStoreInsertPt);
540 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
542 origArg.setType(fieldTy);
544 entry.insertArgument(blockArgIdx + f, fieldTy, loc);
547 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
548 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
551 cir::StoreOp::create(builder, loc, entry.getArgument(blockArgIdx + f),
555 blockArgIdx += numFields;
559 mlir::BlockArgument blockArg = entry.getArgument(blockArgIdx);
568 unsigned numFields = flatTy.getNumElements();
569 assert(numFields >= 2 &&
"getFlattenedCoercedType guarantees >1 fields");
570 Type origTy = blockArg.getType();
571 Location loc = funcOp.getLoc();
574 blockArg.setType(flatTy.getElementType(0));
575 for (
unsigned f = 1; f < numFields; ++f)
576 entry.insertArgument(blockArgIdx + f, flatTy.getElementType(f), loc);
579 builder.setInsertionPointToStart(&entry);
580 auto flatPtrTy = cir::PointerType::get(flatTy);
581 uint64_t flatAlign = dl.getTypeABIAlignment(flatTy);
582 auto flatSlot = cir::AllocaOp::create(
583 builder, loc, flatPtrTy, builder.getStringAttr(
"coerce"),
584 builder.getI64IntegerAttr(flatAlign));
585 SmallPtrSet<Operation *, 8> flattenOps = {flatSlot};
586 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
587 Type fieldPtrTy = cir::PointerType::get(fieldTy);
588 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
591 flattenOps.insert(fieldPtr);
592 auto storeOp = cir::StoreOp::create(
593 builder, loc, entry.getArgument(blockArgIdx + f), fieldPtr);
594 flattenOps.insert(storeOp);
597 cir::LoadOp::create(builder, loc, flatTy, flatSlot.getResult());
598 flattenOps.insert(flatLoaded);
602 Value finalVal = flatLoaded;
603 if (origTy != flatTy) {
604 SmallPtrSet<Operation *, 4> coercionOps;
605 finalVal = emitCoercion(builder, loc, origTy, flatLoaded, &entry, dl,
607 flattenOps.insert(coercionOps.begin(), coercionOps.end());
612 blockArg.replaceAllUsesExcept(finalVal, flattenOps);
614 blockArgIdx += numFields;
618 if (ac.kind == ArgKind::Direct && ac.coercedType) {
619 mlir::Type oldArgTy = blockArg.getType();
620 mlir::Type newArgTy = ac.coercedType;
621 if (oldArgTy == newArgTy) {
625 blockArg.setType(newArgTy);
627 builder.setInsertionPointToStart(&entry);
628 SmallPtrSet<mlir::Operation *, 4> coercionOps;
629 mlir::Value adapted = emitCoercion(builder, funcOp.getLoc(), oldArgTy,
630 blockArg, &entry, dl, coercionOps);
636 blockArg.replaceAllUsesExcept(adapted, coercionOps);
637 }
else if (ac.kind == ArgKind::Indirect) {
642 auto ptrTy = cir::PointerType::get(blockArg.getType());
655 cir::StoreOp paramStore;
656 cir::AllocaOp destAlloca;
657 if (!blockArg.use_empty()) {
658 assert(blockArg.hasOneUse() &&
659 "byref arg must have exactly one use (the CIRGen param "
661 paramStore = cast<cir::StoreOp>(*blockArg.user_begin());
662 assert(paramStore.getValue() == blockArg &&
663 "byref arg's use must be the value operand of its store");
665 cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
672 blockArg.setType(ptrTy);
675 destAlloca.getResult().replaceAllUsesWith(blockArg);
681 blockArg.setType(ptrTy);
683 builder.setInsertionPointToStart(&entry);
684 auto loadOp = cir::LoadOp::create(builder, funcOp.getLoc(), blockArg);
685 SmallPtrSet<mlir::Operation *, 1> loadOps = {loadOp};
686 blockArg.replaceAllUsesExcept(loadOp.getResult(), loadOps);
723void insertSRetStores(mlir::FunctionOpInterface funcOp, mlir::Type origRetTy,
724 mlir::OpBuilder &builder) {
725 mlir::Value sretPtr = funcOp.getArguments()[0];
728 funcOp->walk([&](cir::ReturnOp retOp) { returnOps.push_back(retOp); });
730 cir::AllocaOp retAlloca =
nullptr;
731 for (cir::ReturnOp retOp : returnOps) {
734 assert(!retOp.getInput().empty() &&
735 "cir.return in sret function must have an operand");
737 cir::LoadOp retLoad =
738 mlir::cast<cir::LoadOp>(retOp.getInput()[0].getDefiningOp());
746 retAlloca = mlir::cast<cir::AllocaOp>(retLoad.getAddr().getDefiningOp());
747 retAlloca.getResult().replaceAllUsesWith(sretPtr);
753 builder.setInsertionPoint(retOp);
754 cir::ReturnOp::create(builder, retOp.getLoc());
756 if (retLoad.use_empty())
775 builder.getNamedAttr(
"llvm.sret", mlir::TypeAttr::get(retTy)));
777 builder.getNamedAttr(
"llvm.align", builder.getI64IntegerAttr(align)));
780 builder.getNamedAttr(
"llvm.noalias", builder.getUnitAttr()));
781 attrs.push_back(builder.getNamedAttr(
"llvm.writable", builder.getUnitAttr()));
783 builder.getNamedAttr(
"llvm.dead_on_unwind", builder.getUnitAttr()));
793void applySretSlotAttrs(cir::CallOp newCall, mlir::ArrayAttr argAttrs,
794 mlir::Type retTy, uint64_t align,
795 mlir::OpBuilder &builder) {
796 mlir::MLIRContext *ctx = newCall->getContext();
798 buildSretSlotAttrs(builder, retTy, align,
false);
801 newArgAttrs.reserve(newCall.getArgOperands().size());
802 newArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
804 llvm::append_range(newArgAttrs, argAttrs);
805 assert(newArgAttrs.size() <= newCall.getArgOperands().size() &&
806 "arg_attrs wider than the rewritten call's operand list");
807 newArgAttrs.resize(newCall.getArgOperands().size(),
808 mlir::DictionaryAttr::get(ctx));
809 newCall->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, newArgAttrs));
816static void prependIndirectCallee(cir::CallOp call,
817 SmallVectorImpl<mlir::Value> &args,
818 mlir::Type retTy, mlir::OpBuilder &builder) {
819 if (!call.isIndirect())
821 mlir::Value calleePtr = call.getIndirectCall();
823 paramTypes.reserve(args.size());
824 llvm::transform(args, std::back_inserter(paramTypes),
825 [](mlir::Value v) {
return v.getType(); });
834 auto calleeFnTy = cast<cir::FuncType>(
835 cast<cir::PointerType>(calleePtr.getType()).getPointee());
836 auto newPtrTy = cir::PointerType::get(
837 cir::FuncType::get(paramTypes, retTy, calleeFnTy.isVarArg()));
838 if (calleePtr.getType() != newPtrTy)
839 calleePtr = cir::CastOp::create(builder, call.getLoc(), newPtrTy,
840 cir::CastKind::bitcast, calleePtr);
841 args.insert(args.begin(), calleePtr);
851void rewriteIndirectReturnCall(cir::CallOp call,
852 const FunctionClassification &fc,
854 mlir::Type origRetTy,
856 mlir::OpBuilder &builder) {
857 mlir::MLIRContext *ctx = call->getContext();
858 auto ptrTy = cir::PointerType::get(origRetTy);
859 builder.setInsertionPoint(call);
860 uint64_t sretAlign = fc.returnInfo.indirectAlign.value();
873 mlir::Value sretSlot =
nullptr;
874 cir::StoreOp reuseStore =
nullptr;
875 if (call.getResult().hasOneUse()) {
876 mlir::Operation *user = *call.getResult().getUsers().begin();
877 if (
auto store = mlir::dyn_cast<cir::StoreOp>(user))
878 if (store.getValue() == call.getResult() &&
879 store.getAddr().getType() == ptrTy &&
880 mlir::DominanceInfo().properlyDominates(store.getAddr(), call)) {
881 sretSlot = store.getAddr();
886 auto alloca = cir::AllocaOp::create(
887 builder, call.getLoc(), ptrTy,
888 builder.getStringAttr(
"sret"),
889 builder.getI64IntegerAttr(sretAlign));
894 sretArgs.push_back(sretSlot);
895 sretArgs.append(newArgs.begin(), newArgs.end());
897 mlir::Type sretVoidTy = cir::VoidType::get(ctx);
898 prependIndirectCallee(call, sretArgs, sretVoidTy, builder);
899 auto newCall = cir::CallOp::create(
900 builder, call.getLoc(), call.getCalleeAttr(), sretVoidTy, sretArgs);
901 for (mlir::NamedAttribute attr : call->getAttrs())
902 if (!newCall->hasAttr(
attr.getName()))
903 newCall->setAttr(
attr.getName(),
attr.getValue());
910 mlir::ArrayAttr argAttrs = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
911 bool needsArgAttrUpdate =
912 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
913 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
914 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
915 getFlattenedCoercedType(ac);
917 if (needsArgAttrUpdate)
918 argAttrs = updateArgAttrs(ctx, origCallArgTypes, argAttrs, fc);
919 applySretSlotAttrs(newCall, argAttrs, origRetTy, sretAlign, builder);
927 builder.setInsertionPointAfter(newCall);
928 auto load = cir::LoadOp::create(builder, call.getLoc(), origRetTy, sretSlot,
933 cir::SyncScopeKindAttr(),
936 call.getResult().replaceAllUsesWith(load);
944 mlir::FunctionOpInterface funcOpInterface,
const FunctionClassification &fc,
945 mlir::OpBuilder &builder) {
951 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(funcOpInterface);
953 if (!fc.needsRewrite())
954 return mlir::success();
958 mlir::MLIRContext *ctx = funcOp->getContext();
963 assert(oldResultTypes.size() <= 1 &&
964 "CIR functions return zero or one value");
967 if (mlir::failed(buildNewArgTypes(oldArgTypes, fc, newArgTypes,
968 [&]() {
return funcOp.emitOpError(); })))
969 return mlir::failure();
971 mlir::Type voidTy = cir::VoidType::get(ctx);
972 mlir::Type origRetTy = oldResultTypes.empty() ? voidTy : oldResultTypes[0];
973 mlir::Type newRetTy = computeNewReturnType(
974 origRetTy, fc.returnInfo, ctx, [&]() { return funcOp.emitOpError(); });
976 return mlir::failure();
986 fc.returnInfo.kind == ArgKind::Indirect && !oldResultTypes.empty();
988 newArgTypes.insert(newArgTypes.begin(), cir::PointerType::get(origRetTy));
990 if (funcOp.isDefinition()) {
991 mlir::Region &body = funcOp->getRegion(0);
997 body.front().insertArgument(0u, cir::PointerType::get(origRetTy),
999 insertSRetStores(funcOp, origRetTy, builder);
1009 insertArgCoercion(funcOp, fc, builder, dl, hasSRet);
1014 if (fc.returnInfo.kind == ArgKind::Direct && fc.returnInfo.coercedType &&
1015 !oldResultTypes.empty() && fc.returnInfo.coercedType != origRetTy)
1016 insertReturnCoercion(funcOp, origRetTy, fc.returnInfo.coercedType,
1019 mlir::Block &entry = body.front();
1028 unsigned blockArgIdx = hasSRet ? 1 : 0;
1029 for (
auto [i, ac] : llvm::enumerate(fc.argInfos)) {
1030 if (blockArgIdx >= entry.getNumArguments())
1032 if (ac.kind == ArgKind::Ignore) {
1033 mlir::BlockArgument arg = entry.getArgument(blockArgIdx);
1034 if (!arg.use_empty()) {
1035 builder.setInsertionPointToStart(&entry);
1036 mlir::Value poison =
1037 createIgnoredValue(builder, funcOp.getLoc(), arg.getType());
1038 arg.replaceAllUsesWith(poison);
1040 entry.eraseArgument(blockArgIdx);
1044 blockArgIdx += flatTy.getNumElements();
1045 else if (ac.kind == ArgKind::Expand)
1046 blockArgIdx += cast<cir::RecordType>(oldArgTypes[i]).getNumElements();
1058 if (fc.returnInfo.kind == ArgKind::Ignore && !oldResultTypes.empty()) {
1059 assert(mlir::isa<cir::VoidType>(newRetTy) &&
1060 "Ignore-return path requires the new return type to be void");
1062 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
1063 for (cir::ReturnOp r : returns) {
1064 if (r.getNumOperands() == 0)
1066 builder.setInsertionPoint(r);
1067 cir::ReturnOp::create(builder, r.getLoc());
1073 mlir::Type newFnTy = funcOp.cloneTypeWith(newArgTypes, newResultTypes);
1074 funcOp.setFunctionTypeAttr(mlir::TypeAttr::get(newFnTy));
1081 bool needsArgAttrUpdate =
1082 hasSRet || llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1083 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1084 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1085 getFlattenedCoercedType(ac);
1087 if (needsArgAttrUpdate) {
1088 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1089 mlir::ArrayAttr updated = updateArgAttrs(ctx, oldArgTypes, existing, fc);
1095 builder, origRetTy, fc.returnInfo.indirectAlign.value(),
1096 funcOp.isDefinition());
1098 withSret.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1099 llvm::append_range(withSret, updated);
1100 funcOp->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, withSret));
1102 funcOp->setAttr(
"arg_attrs", updated);
1108 if (fc.returnInfo.kind == ArgKind::Extend) {
1109 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1110 funcOp->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1113 return mlir::success();
1118 const FunctionClassification &fc,
1119 mlir::OpBuilder &builder) {
1129 unsigned numOperands =
1130 mlir::cast<cir::CIRCallOpInterface>(callOp).getNumArgOperands();
1131 if (numOperands > fc.argInfos.size())
1132 return callOp->emitOpError()
1133 <<
"variadic arguments not yet implemented in CallConvLowering";
1134 if (numOperands < fc.argInfos.size())
1135 return callOp->emitOpError()
1136 <<
"call passes fewer arguments than the callee declares, which is "
1137 "not yet implemented in CallConvLowering";
1139 if (!fc.needsRewrite())
1140 return mlir::success();
1142 if (mlir::isa<cir::TryCallOp>(callOp))
1143 return callOp->emitOpError()
1144 <<
"TryCallOp not yet implemented in CallConvLowering";
1146 auto call = mlir::cast<cir::CallOp>(callOp);
1147 mlir::MLIRContext *ctx = callOp->getContext();
1148 mlir::Block *slotBlock = coercionSlotBlock(call);
1150 builder.setInsertionPoint(call);
1153 mlir::ValueRange argOperands = call.getArgOperands();
1154 newArgs.reserve(argOperands.size());
1165 llvm::append_range(origCallArgTypes, argOperands.getTypes());
1166 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1167 if (ac.kind == ArgKind::Ignore)
1169 mlir::Value arg = argOperands[idx];
1177 if (arg.getType() != flatTy) {
1178 SmallPtrSet<mlir::Operation *, 4> coercionOps;
1179 mlir::Value coercedPtr = emitCoercionToMemory(
1180 builder, call.getLoc(), flatTy, arg, slotBlock, dl, coercionOps);
1181 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
1182 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
1184 cir::GetMemberOp::create(builder, call.getLoc(), fieldPtrTy,
1186 newArgs.push_back(cir::LoadOp::create(builder, call.getLoc(), fieldTy,
1187 fieldPtr.getResult()));
1190 emitStructFieldArgs(builder, call.getLoc(), arg, flatTy, newArgs,
1191 replacedWholeLoads);
1193 }
else if (ac.kind == ArgKind::Expand) {
1196 auto recTy = cast<cir::RecordType>(arg.getType());
1198 "Expand classification requires a struct type, not a union");
1199 emitStructFieldArgs(builder, call.getLoc(), arg, recTy, newArgs,
1200 replacedWholeLoads);
1201 }
else if (ac.kind == ArgKind::Direct && ac.coercedType &&
1202 arg.getType() != ac.coercedType) {
1203 arg = emitCoercion(builder, call.getLoc(), ac.coercedType, arg, slotBlock,
1205 newArgs.push_back(arg);
1206 }
else if (ac.kind == ArgKind::Indirect) {
1212 mlir::Type argTy = arg.getType();
1213 auto ptrTy = cir::PointerType::get(argTy);
1214 uint64_t align = ac.indirectAlign.value();
1215 StringRef slotName = ac.byVal ?
"byval" :
"byref";
1216 auto slot = cir::AllocaOp::create(builder, call.getLoc(), ptrTy,
1217 builder.getStringAttr(slotName),
1218 builder.getI64IntegerAttr(align));
1219 cir::StoreOp::create(builder, call.getLoc(), arg, slot);
1221 newArgs.push_back(arg);
1223 newArgs.push_back(arg);
1227 bool hasResult = call.getNumResults() > 0;
1228 mlir::Type origRetTy =
1229 hasResult ? call.getResult().getType() : cir::VoidType::get(ctx);
1235 if (fc.returnInfo.kind == ArgKind::Indirect && hasResult) {
1236 rewriteIndirectReturnCall(call, fc, newArgs, origRetTy, origCallArgTypes,
1238 return mlir::success();
1241 mlir::Type callRetTy = origRetTy;
1242 if (fc.returnInfo.kind == ArgKind::Ignore && hasResult)
1243 callRetTy = cir::VoidType::get(ctx);
1244 bool returnNeedsCoercion =
1245 hasResult && fc.returnInfo.kind == ArgKind::Direct &&
1246 fc.returnInfo.coercedType && fc.returnInfo.coercedType != origRetTy;
1247 if (returnNeedsCoercion)
1248 callRetTy = fc.returnInfo.coercedType;
1250 builder.setInsertionPoint(call);
1251 prependIndirectCallee(call, newArgs, callRetTy, builder);
1252 auto newCall = cir::CallOp::create(builder, call.getLoc(),
1253 call.getCalleeAttr(), callRetTy, newArgs);
1254 for (mlir::NamedAttribute attr : call->getAttrs())
1255 if (!newCall->hasAttr(attr.getName()))
1256 newCall->setAttr(attr.getName(), attr.getValue());
1260 if (returnNeedsCoercion) {
1261 builder.setInsertionPointAfter(newCall);
1262 mlir::Value coercedBack = emitCoercion(builder, call.getLoc(), origRetTy,
1263 newCall.getResult(), slotBlock, dl);
1264 call.getResult().replaceAllUsesWith(coercedBack);
1271 bool needsArgAttrUpdate =
1272 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1273 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1274 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1275 getFlattenedCoercedType(ac);
1277 if (needsArgAttrUpdate) {
1278 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1279 newCall->setAttr(
"arg_attrs",
1280 updateArgAttrs(ctx, origCallArgTypes, existing, fc));
1282 if (fc.returnInfo.kind == ArgKind::Extend) {
1283 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1284 newCall->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1287 if (hasResult && fc.returnInfo.kind == ArgKind::Ignore) {
1292 if (!call.getResult().use_empty()) {
1293 builder.setInsertionPointAfter(newCall);
1294 mlir::Value poison =
1295 createIgnoredValue(builder, call.getLoc(), origRetTy);
1296 call.getResult().replaceAllUsesWith(poison);
1298 }
else if (hasResult && !returnNeedsCoercion) {
1300 call.getResult().replaceAllUsesWith(newCall.getResult());
1310 SmallPtrSet<mlir::Operation *, 4> erased;
1311 for (cir::LoadOp wholeLoad : replacedWholeLoads)
1312 if (erased.insert(wholeLoad).second && wholeLoad.use_empty())
1315 return mlir::success();
1320 mlir::OpBuilder &builder) {
1321 auto oldPtrTy = mlir::cast<cir::PointerType>(addrOp.getAddr().getType());
1322 cir::FuncType newFuncTy = funcOp.getFunctionType();
1325 if (newFuncTy == oldPtrTy.getPointee())
1329 addrOp.getAddr().setType(cir::PointerType::get(newFuncTy));
1330 if (addrOp.getAddr().use_empty())
1337 mlir::OpBuilder::InsertionGuard guard(builder);
1338 builder.setInsertionPointAfter(addrOp);
1339 auto bitcast = cir::CastOp::create(builder, addrOp.getLoc(), oldPtrTy,
1340 cir::CastKind::bitcast, addrOp.getAddr());
1341 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