10#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
11#include "mlir/IR/Builders.h"
12#include "mlir/IR/Dominance.h"
19#include "llvm/ADT/STLExtras.h"
20#include "llvm/Support/MathExtras.h"
27using namespace mlir::abi;
75 if (ac.kind != ArgKind::Direct || !ac.coercedType || !ac.canFlatten)
77 auto recTy = dyn_cast<cir::RecordType>(ac.coercedType);
78 if (!recTy || !recTy.isStruct() || recTy.getNumElements() <= 1)
90 const FunctionClassification &fc,
91 SmallVectorImpl<mlir::Type> &newArgTypes,
92 function_ref<mlir::InFlightDiagnostic()> emitError) {
93 assert(newArgTypes.empty() &&
"expected an empty output vector");
94 newArgTypes.reserve(oldArgTypes.size());
95 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
96 mlir::Type origTy = oldArgTypes[idx];
104 llvm::append_range(newArgTypes, flatTy.getMembers());
110 newArgTypes.push_back(ac.coercedType ? ac.coercedType : origTy);
113 case ArgKind::Ignore:
115 case ArgKind::Expand: {
119 auto recTy = cast<cir::RecordType>(origTy);
120 assert(recTy.isStruct() &&
121 "Expand classification requires a struct type, not a union");
122 assert(!recTy.getMembers().empty() &&
123 "Expand classification requires at least one struct field");
124 llvm::append_range(newArgTypes, recTy.getMembers());
127 case ArgKind::Extend:
134 newArgTypes.push_back(origTy);
136 case ArgKind::Indirect:
137 newArgTypes.push_back(cir::PointerType::get(origTy));
141 return mlir::success();
149computeNewReturnType(mlir::Type origRetTy,
const ArgClassification &retInfo,
150 mlir::MLIRContext *ctx,
151 function_ref<mlir::InFlightDiagnostic()> emitError) {
152 switch (retInfo.kind) {
153 case ArgKind::Direct:
156 return retInfo.coercedType ? retInfo.coercedType : origRetTy;
157 case ArgKind::Ignore:
158 return cir::VoidType::get(ctx);
159 case ArgKind::Expand:
160 emitError() <<
"Expand return is not allowed (classic codegen rejects "
161 <<
"it in EmitFunctionEpilog)";
163 case ArgKind::Extend:
168 case ArgKind::Indirect:
173 return cir::VoidType::get(ctx);
175 llvm_unreachable(
"all ArgKind cases handled");
183mlir::Value createIgnoredValue(mlir::OpBuilder &builder, mlir::Location loc,
185 return cir::ConstantOp::create(builder, loc, ty, cir::PoisonAttr::get(ty));
193static bool isCoercionWiderThanMemory(mlir::Type origTy, mlir::Type coercedTy,
194 const mlir::DataLayout &dl) {
195 uint64_t memoryBits = dl.getTypeSizeInBits(origTy).getFixedValue();
196 if (
auto vecTy = mlir::dyn_cast<cir::VectorType>(origTy);
197 vecTy && mlir::isa<cir::BoolType>(vecTy.getElementType()))
198 memoryBits = vecTy.getBoolStorageWidth();
199 return dl.getTypeSizeInBits(coercedTy).getFixedValue() > memoryBits;
206static bool needsArgAttrUpdate(
const FunctionClassification &fc) {
207 return llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
208 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
209 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
210 (ac.kind == ArgKind::Direct && ac.coercedType);
220mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx,
222 mlir::ArrayAttr existingArgAttrs,
223 const FunctionClassification &fc,
224 const mlir::DataLayout &dl) {
225 mlir::Builder builder(ctx);
227 newArgAttrs.reserve(fc.argInfos.size());
228 for (
auto [oldIdx, ac] : llvm::enumerate(fc.argInfos)) {
229 if (ac.kind == ArgKind::Ignore)
231 mlir::DictionaryAttr existing = builder.getDictionaryAttr({});
232 if (existingArgAttrs && oldIdx < existingArgAttrs.size())
233 existing = mlir::cast<mlir::DictionaryAttr>(existingArgAttrs[oldIdx]);
237 newArgAttrs.append(flatTy.getNumElements(),
238 builder.getDictionaryAttr({}));
239 }
else if (ac.kind == ArgKind::Expand) {
242 auto recTy = cast<cir::RecordType>(origArgTypes[oldIdx]);
243 newArgAttrs.append(recTy.getNumElements(), builder.getDictionaryAttr({}));
244 }
else if (ac.kind == ArgKind::Extend) {
245 StringRef attrName = ac.signExtend
246 ? mlir::LLVM::LLVMDialect::getSExtAttrName()
247 :
mlir::LLVM::LLVMDialect::getZExtAttrName();
248 mlir::NamedAttrList attrs(existing);
249 attrs.set(attrName, builder.getUnitAttr());
250 newArgAttrs.push_back(attrs.getDictionary(ctx));
251 }
else if (ac.kind == ArgKind::Indirect) {
258 mlir::Type pointeeTy = origArgTypes[oldIdx];
259 mlir::NamedAttrList attrs(existing);
260 attrs.set(mlir::LLVM::LLVMDialect::getAlignAttrName(),
261 builder.getI64IntegerAttr(ac.indirectAlign.value()));
262 attrs.set(mlir::LLVM::LLVMDialect::getNoUndefAttrName(),
263 builder.getUnitAttr());
269 attrs.set(mlir::LLVM::LLVMDialect::getByValAttrName(),
270 mlir::TypeAttr::get(pointeeTy));
278 attrs.set(mlir::LLVM::LLVMDialect::getNoFreeObjAttrName(),
279 builder.getUnitAttr());
280 attrs.set(mlir::LLVM::LLVMDialect::getDereferenceableAttrName(),
281 builder.getI64IntegerAttr(
282 dl.getTypeSize(pointeeTy).getFixedValue()));
284 newArgAttrs.push_back(attrs.getDictionary(ctx));
285 }
else if (ac.kind == ArgKind::Direct && ac.coercedType &&
286 isCoercionWiderThanMemory(origArgTypes[oldIdx], ac.coercedType,
288 mlir::NamedAttrList attrs(existing);
289 attrs.erase(mlir::LLVM::LLVMDialect::getNoUndefAttrName());
290 newArgAttrs.push_back(attrs.getDictionary(ctx));
292 newArgAttrs.push_back(existing);
295 return builder.getArrayAttr(newArgAttrs);
304mlir::ArrayAttr updateResAttrs(mlir::MLIRContext *ctx,
305 mlir::ArrayAttr existingResAttrs,
306 const ArgClassification &retInfo,
307 mlir::Type origRetTy,
308 const mlir::DataLayout &dl) {
309 bool needsNoUndefDrop =
310 retInfo.kind == ArgKind::Direct && retInfo.coercedType &&
311 isCoercionWiderThanMemory(origRetTy, retInfo.coercedType, dl);
312 if (retInfo.kind != ArgKind::Extend && !needsNoUndefDrop)
313 return existingResAttrs;
315 mlir::NamedAttrList attrs;
316 if (existingResAttrs && !existingResAttrs.empty())
318 mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0]).getValue());
319 if (retInfo.kind == ArgKind::Extend)
320 attrs.set(retInfo.signExtend ?
"llvm.signext" :
"llvm.zeroext",
321 mlir::UnitAttr::get(ctx));
322 if (needsNoUndefDrop)
323 attrs.erase(mlir::LLVM::LLVMDialect::getNoUndefAttrName());
324 return mlir::ArrayAttr::get(ctx, {attrs.getDictionary(ctx)});
337static uint64_t coercionByteSize(mlir::Type ty,
const mlir::DataLayout &dl) {
338 if (
auto intTy = mlir::dyn_cast<cir::IntType>(ty))
339 return llvm::divideCeil(intTy.getWidth(), 8);
340 return dl.getTypeSize(ty);
349emitViewAtOffset(mlir::OpBuilder &builder, mlir::Location loc,
350 mlir::Type wantTy, mlir::Value base,
351 SmallPtrSetImpl<mlir::Operation *> &createdOps,
353 auto basePtrTy = mlir::cast<cir::PointerType>(base.getType());
354 if (wantTy == basePtrTy.getPointee()) {
355 assert(offset == 0 &&
"a view of the storage's own type has no offset");
358 mlir::ptr::MemorySpaceAttrInterface addrSpace = basePtrTy.getAddrSpace();
359 mlir::Value view = base;
361 auto u8Ty = cir::IntType::get(builder.getContext(), 8,
false);
362 auto u8PtrTy = cir::PointerType::get(u8Ty, addrSpace);
363 auto u8Base = cir::CastOp::create(builder, loc, u8PtrTy,
364 cir::CastKind::bitcast, base);
365 createdOps.insert(u8Base);
367 cir::IntType::get(builder.getContext(), 64,
true);
368 auto strideVal = cir::ConstantOp::create(
369 builder, loc, cir::IntAttr::get(strideTy, offset));
370 createdOps.insert(strideVal);
371 view = cir::PtrStrideOp::create(builder, loc, u8PtrTy, u8Base, strideVal);
372 createdOps.insert(view.getDefiningOp());
374 auto typedView = cir::CastOp::create(builder, loc,
375 cir::PointerType::get(wantTy, addrSpace),
376 cir::CastKind::bitcast, view);
377 createdOps.insert(typedView);
383static cir::PointerType getAllocaPtrTy(mlir::Type pointee,
384 const mlir::DataLayout &dl) {
385 mlir::ptr::MemorySpaceAttrInterface addrSpace;
386 if (
auto asAttr = mlir::dyn_cast_if_present<mlir::IntegerAttr>(
387 dl.getAllocaMemorySpace()))
388 addrSpace = cir::TargetAddressSpaceAttr::get(pointee.getContext(),
390 return cir::PointerType::get(pointee, addrSpace);
398static cir::AllocaOp createCoercionSlot(
399 mlir::OpBuilder &builder, mlir::Location loc, mlir::Type srcTy,
400 mlir::Type dstTy, mlir::Block *slotBlock,
const mlir::DataLayout &dl,
401 SmallPtrSetImpl<mlir::Operation *> &createdOps,
unsigned offset) {
402 assert(srcTy != dstTy &&
"callers must pre-check that the types differ");
404 uint64_t srcAlign = dl.getTypeABIAlignment(srcTy);
405 uint64_t dstAlign = dl.getTypeABIAlignment(dstTy);
406 uint64_t allocaAlign = std::max(srcAlign, dstAlign);
407 mlir::Type slotTy = coercionByteSize(srcTy, dl) >= coercionByteSize(dstTy, dl)
414 [[maybe_unused]] mlir::Type coercedTy = ((slotTy == srcTy) ? dstTy : srcTy);
415 assert((offset == 0 ||
416 coercionByteSize(coercedTy, dl) < coercionByteSize(slotTy, dl)) &&
417 "a direct offset must land on the coerced side, the smaller one");
418 assert((offset == 0 ||
419 offset + dl.getTypeSize(coercedTy) <= dl.getTypeSize(slotTy)) &&
420 "coerce slot too small for offset access");
421 assert((offset == 0 || offset % dl.getTypeABIAlignment(coercedTy) == 0) &&
422 "a direct offset must be aligned for the coerced access");
424 mlir::OpBuilder::InsertionGuard guard(builder);
425 builder.setInsertionPointToStart(slotBlock);
426 auto alloca = cir::AllocaOp::create(builder, loc, getAllocaPtrTy(slotTy, dl),
427 builder.getStringAttr(
"coerce"),
428 builder.getI64IntegerAttr(allocaAlign));
429 createdOps.insert(alloca);
463mlir::Value emitCoercionToMemory(mlir::OpBuilder &builder, mlir::Location loc,
464 mlir::Type dstTy, mlir::Value src,
465 mlir::Block *slotBlock,
466 const mlir::DataLayout &dl,
467 SmallPtrSetImpl<mlir::Operation *> &createdOps,
469 mlir::Type srcTy = src.getType();
470 cir::AllocaOp alloca = createCoercionSlot(builder, loc, srcTy, dstTy,
471 slotBlock, dl, createdOps, offset);
472 mlir::Type slotTy = alloca.getAllocaType();
475 mlir::Value srcSlot = emitViewAtOffset(
476 builder, loc, srcTy, alloca, createdOps, srcTy == slotTy ? 0 : offset);
477 auto store = cir::StoreOp::create(builder, loc, src, srcSlot);
478 createdOps.insert(store);
481 return emitViewAtOffset(builder, loc, dstTy, alloca, createdOps,
482 dstTy == slotTy ? 0 : offset);
488mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
489 mlir::Type dstTy, mlir::Value src,
490 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
491 SmallPtrSetImpl<mlir::Operation *> &createdOps,
493 mlir::Value dstSlot = emitCoercionToMemory(builder, loc, dstTy, src,
494 slotBlock, dl, createdOps, offset);
495 auto load = cir::LoadOp::create(builder, loc, dstSlot);
496 createdOps.insert(load);
502mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
503 mlir::Type dstTy, mlir::Value src,
504 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
506 SmallPtrSet<mlir::Operation *, 4> ignored;
507 return emitCoercion(builder, loc, dstTy, src, slotBlock, dl, ignored, offset);
519mlir::Block *coercionSlotBlock(mlir::Operation *op) {
520 if (
auto funcOp = op->getParentOfType<mlir::FunctionOpInterface>())
521 return &funcOp->getRegion(0).front();
522 mlir::Region *region = op->getParentRegion();
523 while (mlir::Region *outer = region->getParentRegion()) {
524 if (mlir::isa<mlir::ModuleOp>(outer->getParentOp()))
528 assert(!region->empty() &&
"coercion slot needs a block to hold the alloca");
529 return ®ion->front();
536static cir::LoadOp maybeGetSimpleLoad(mlir::Value val) {
537 cir::LoadOp load = val.getDefiningOp<cir::LoadOp>();
538 if (!load || load.getIsVolatile() || load.getMemOrder())
547static bool canReadInPlace(cir::LoadOp load, mlir::Type coercedTy,
561static mlir::Value emitInPlaceCoercedLoad(mlir::OpBuilder &builder,
562 mlir::Location loc, cir::LoadOp load,
563 mlir::Type coercedTy,
566 assert(src &&
"the record load must read an alloca");
567 mlir::OpBuilder::InsertionGuard guard(builder);
568 builder.setInsertionPoint(load);
569 SmallPtrSet<mlir::Operation *, 4> ignored;
570 mlir::Value view = emitViewAtOffset(builder, loc, coercedTy, load.getAddr(),
572 auto coerced = cir::LoadOp::create(builder, loc, view);
573 coerced.setAlignment(llvm::MinAlign(src.getAlignment(), offset));
582static mlir::Value emitCopiedCoercedLoad(mlir::OpBuilder &builder,
583 mlir::Location loc, cir::LoadOp load,
584 mlir::Type coercedTy,
585 mlir::Block *slotBlock,
586 const mlir::DataLayout &dl,
588 auto recTy = cast<cir::RecordType>(load.getType());
591 srcAlign = src.getAlignment();
593 srcAlign = load.getAlignment().value_or(dl.getTypeABIAlignment(recTy));
594 SmallPtrSet<mlir::Operation *, 4> ignored;
595 cir::AllocaOp slot = createCoercionSlot(builder, loc, recTy, coercedTy,
596 slotBlock, dl, ignored, offset);
597 slot.setAlignment(std::max(slot.getAlignment(), srcAlign));
598 assert((offset == 0 || slot.getAllocaType() == recTy) &&
599 "a direct offset must leave the record as the slot's type");
600 mlir::OpBuilder::InsertionGuard guard(builder);
601 builder.setInsertionPoint(load);
602 mlir::Value recView = emitViewAtOffset(builder, loc, recTy, slot, ignored, 0);
603 cir::CopyOp::create(builder, loc, recView, load.getAddr(),
604 builder.getI64IntegerAttr(slot.getAlignment()),
605 builder.getI64IntegerAttr(srcAlign));
606 mlir::Value coercedView =
607 emitViewAtOffset(builder, loc, coercedTy, slot, ignored, offset);
608 auto coerced = cir::LoadOp::create(builder, loc, coercedView);
609 coerced.setAlignment(llvm::MinAlign(slot.getAlignment(), offset));
620void insertReturnCoercion(mlir::FunctionOpInterface funcOp,
621 mlir::Type origRetTy, mlir::Type coercedRetTy,
622 mlir::OpBuilder &builder,
const mlir::DataLayout &dl,
626 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
627 for (cir::ReturnOp r : returns) {
628 if (r.getInput().empty())
630 mlir::Value origVal = r.getInput()[0];
631 if (origVal.getType() == coercedRetTy)
634 cir::LoadOp recLoad = maybeGetSimpleLoad(origVal);
635 if (recLoad && mlir::isa<cir::RecordType>(recLoad.getType())) {
636 mlir::Value coerced =
637 canReadInPlace(recLoad, coercedRetTy, cirDataLayout, offset)
638 ? emitInPlaceCoercedLoad(builder, r.getLoc(), recLoad,
639 coercedRetTy, offset)
640 : emitCopiedCoercedLoad(
641 builder, r.getLoc(), recLoad, coercedRetTy,
642 &funcOp->getRegion(0).front(), dl, offset);
643 r->setOperand(0, coerced);
644 if (recLoad->use_empty())
649 builder.setInsertionPoint(r);
650 mlir::Value coerced =
651 emitCoercion(builder, r.getLoc(), coercedRetTy, origVal,
652 &funcOp->getRegion(0).front(), dl, offset);
653 r->setOperand(0, coerced);
659static cir::LoadOp getWholeRecordLoad(mlir::Value recordVal) {
660 cir::LoadOp load = maybeGetSimpleLoad(recordVal);
686static cir::LoadOp forwardableIndirectLoad(cir::LoadOp load,
688 if (!load || load.getAddr().getType() !=
689 cir::PointerType::get(load.getResult().getType()))
692 return slot && slot.getAlignment() >= minAlign ? load : cir::LoadOp();
705static bool storageUnwrittenBetween(cir::LoadOp load, mlir::Operation *call,
706 mlir::AliasAnalysis &aliasAnalysis) {
707 if (load->getBlock() != call->getBlock())
709 for (mlir::Operation *op = load->getNextNode(); op != call;
710 op = op->getNextNode()) {
711 assert(op &&
"the load must precede the call in the block they share");
712 if (op->getNumRegions() != 0 ||
713 aliasAnalysis.getModRef(op, load.getAddr()).isMod())
723static mlir::IntegerAttr loadSourceAlignment(cir::LoadOp load,
724 mlir::OpBuilder &builder) {
725 if (mlir::IntegerAttr stated = load.getAlignmentAttr())
728 return builder.getI64IntegerAttr(slot.getAlignment());
729 return builder.getI64IntegerAttr(1);
743static void emitStructFieldArgs(mlir::OpBuilder &builder, mlir::Location loc,
745 SmallVectorImpl<mlir::Value> &newArgs,
746 SmallVectorImpl<cir::LoadOp> &deadRecordLoads) {
747 cir::LoadOp srcLoad = getWholeRecordLoad(structVal);
750 mlir::OpBuilder::InsertionGuard guard(builder);
751 builder.setInsertionPoint(srcLoad);
752 cir::PointerType baseTy = srcLoad.getAddr().getType();
753 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
754 mlir::Type fieldPtrTy =
755 cir::PointerType::get(fieldTy, baseTy.getAddrSpace());
756 mlir::Value fieldPtr = cir::GetMemberOp::create(
757 builder, loc, fieldPtrTy, srcLoad.getAddr(),
"",
759 newArgs.push_back(cir::LoadOp::create(builder, loc, fieldPtr));
761 deadRecordLoads.push_back(srcLoad);
765 cir::ExtractMemberOp::create(builder, loc, structVal, f));
776 for (mlir::Operation *load : uniqueLoads)
777 if (load->use_empty())
783static cir::StoreOp maybeFindParamSpill(mlir::BlockArgument blockArg) {
784 if (!blockArg.hasOneUse())
786 auto store = dyn_cast<cir::StoreOp>(*blockArg.user_begin());
787 if (!store || store.getValue() != blockArg)
798static std::pair<cir::StoreOp, cir::AllocaOp>
799findParamSpill(mlir::BlockArgument blockArg) {
800 if (blockArg.use_empty())
802 cir::StoreOp store = maybeFindParamSpill(blockArg);
803 assert(store &&
"non-byval arg's only use must be its CIRGen param spill");
804 return {store, cast<cir::AllocaOp>(store.getAddr().getDefiningOp())};
825void insertArgCoercion(
826 mlir::FunctionOpInterface funcOp,
const FunctionClassification &fc,
827 mlir::OpBuilder &builder,
const mlir::DataLayout &dl,
bool hasSRetArg,
828 SmallVectorImpl<std::pair<cir::AllocaOp, mlir::BlockArgument>>
829 &pendingParamSlots) {
830 mlir::Region &body = funcOp->getRegion(0);
833 mlir::Block &entry = body.front();
839 unsigned blockArgIdx = hasSRetArg ? 1 : 0;
841 for (
const ArgClassification &ac : fc.argInfos) {
842 assert(blockArgIdx < entry.getNumArguments() &&
843 "classification count must not exceed entry block arguments");
845 if (ac.kind == ArgKind::Expand) {
849 mlir::BlockArgument origArg = entry.getArgument(blockArgIdx);
850 auto recTy = cast<cir::RecordType>(origArg.getType());
852 "Expand classification requires a struct type, not a union");
854 assert(numFields > 0 &&
855 "Expand classification requires at least one struct field");
856 mlir::Location loc = funcOp.getLoc();
866 cir::StoreOp paramStore;
867 cir::AllocaOp destAlloca;
868 if (!origArg.use_empty()) {
869 assert(origArg.hasOneUse() &&
870 "Expand arg must have exactly one use (the CIRGen param spill)");
871 paramStore = cast<cir::StoreOp>(*origArg.user_begin());
872 assert(paramStore.getValue() == origArg &&
873 "Expand arg's use must be the value operand of its store");
874 destAlloca = cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
881 mlir::Operation *fieldStoreInsertPt =
nullptr;
883 fieldStoreInsertPt = paramStore->getNextNode();
884 assert(fieldStoreInsertPt &&
885 "param spill must be followed by a block terminator");
897 builder.setInsertionPoint(fieldStoreInsertPt);
898 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
900 origArg.setType(fieldTy);
902 entry.insertArgument(blockArgIdx + f, fieldTy, loc);
905 mlir::Type fieldPtrTy = cir::PointerType::get(
907 mlir::cast<cir::PointerType>(destAlloca.getType()).getAddrSpace());
908 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
911 cir::StoreOp::create(builder, loc, entry.getArgument(blockArgIdx + f),
915 blockArgIdx += numFields;
919 mlir::BlockArgument blockArg = entry.getArgument(blockArgIdx);
928 unsigned numFields = flatTy.getNumElements();
929 assert(numFields >= 2 &&
"getFlattenedCoercedType guarantees >1 fields");
930 Type origTy = blockArg.getType();
931 Location loc = funcOp.getLoc();
934 blockArg.setType(flatTy.getElementType(0));
935 for (
unsigned f = 1; f < numFields; ++f)
936 entry.insertArgument(blockArgIdx + f, flatTy.getElementType(f), loc);
939 builder.setInsertionPointToStart(&entry);
940 cir::PointerType flatPtrTy = getAllocaPtrTy(flatTy, dl);
941 uint64_t flatAlign = dl.getTypeABIAlignment(flatTy);
942 auto flatSlot = cir::AllocaOp::create(
943 builder, loc, flatPtrTy, builder.getStringAttr(
"coerce"),
944 builder.getI64IntegerAttr(flatAlign));
945 SmallPtrSet<Operation *, 8> flattenOps = {flatSlot};
946 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
948 cir::PointerType::get(fieldTy, flatPtrTy.getAddrSpace());
949 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
952 flattenOps.insert(fieldPtr);
953 auto storeOp = cir::StoreOp::create(
954 builder, loc, entry.getArgument(blockArgIdx + f), fieldPtr);
955 flattenOps.insert(storeOp);
958 cir::LoadOp::create(builder, loc, flatTy, flatSlot.getResult());
959 flattenOps.insert(flatLoaded);
963 Value finalVal = flatLoaded;
964 if (origTy != flatTy) {
965 SmallPtrSet<Operation *, 4> coercionOps;
966 assert(!ac.directOffset &&
967 "each field is read from slot offset 0 here, so a flattened "
968 "coercion cannot honor a direct offset");
969 finalVal = emitCoercion(builder, loc, origTy, flatLoaded, &entry, dl,
971 flattenOps.insert(coercionOps.begin(), coercionOps.end());
976 blockArg.replaceAllUsesExcept(finalVal, flattenOps);
978 blockArgIdx += numFields;
982 if (ac.kind == ArgKind::Direct && ac.coercedType) {
983 mlir::Type oldArgTy = blockArg.getType();
984 mlir::Type newArgTy = ac.coercedType;
985 if (oldArgTy == newArgTy) {
989 blockArg.setType(newArgTy);
991 builder.setInsertionPointToStart(&entry);
992 SmallPtrSet<mlir::Operation *, 4> coercionOps;
993 mlir::Value adapted =
994 emitCoercion(builder, funcOp.getLoc(), oldArgTy, blockArg, &entry, dl,
995 coercionOps, ac.directOffset);
1001 blockArg.replaceAllUsesExcept(adapted, coercionOps);
1002 }
else if (ac.kind == ArgKind::Indirect) {
1009 auto ptrTy = cir::PointerType::get(blockArg.getType());
1017 auto [paramStore, destAlloca] = findParamSpill(blockArg);
1020 paramStore->erase();
1023 blockArg.setType(ptrTy);
1032 pendingParamSlots.emplace_back(destAlloca, blockArg);
1039 cir::StoreOp paramStore;
1040 if (mlir::isa<cir::RecordType>(blockArg.getType()))
1041 paramStore = maybeFindParamSpill(blockArg);
1045 mlir::Value destAddr;
1046 mlir::IntegerAttr destAlign;
1047 mlir::Operation *spillPos =
nullptr;
1049 destAddr = paramStore.getAddr();
1051 destAlign = destAlloca.getAlignmentAttr();
1052 spillPos = paramStore->getNextNode();
1053 assert(spillPos &&
"param spill must be followed by a block "
1055 paramStore->erase();
1058 blockArg.setType(ptrTy);
1061 mlir::OpBuilder::InsertionGuard guard(builder);
1062 builder.setInsertionPoint(spillPos);
1063 cir::CopyOp::create(
1064 builder, funcOp.getLoc(), destAddr, blockArg, destAlign,
1065 builder.getI64IntegerAttr(ac.indirectAlign.value()));
1066 }
else if (!blockArg.use_empty()) {
1068 builder.setInsertionPointToStart(&entry);
1069 auto loadOp = cir::LoadOp::create(builder, funcOp.getLoc(), blockArg);
1070 SmallPtrSet<mlir::Operation *, 1> loadOps = {loadOp};
1071 blockArg.replaceAllUsesExcept(loadOp.getResult(), loadOps);
1109void insertSRetStores(mlir::FunctionOpInterface funcOp, mlir::Type origRetTy,
1110 mlir::OpBuilder &builder) {
1111 mlir::Value sretPtr = funcOp.getArguments()[0];
1114 funcOp->walk([&](cir::ReturnOp retOp) { returnOps.push_back(retOp); });
1116 cir::AllocaOp retAlloca =
nullptr;
1117 for (cir::ReturnOp retOp : returnOps) {
1120 assert(!retOp.getInput().empty() &&
1121 "cir.return in sret function must have an operand");
1123 cir::LoadOp retLoad =
1124 mlir::cast<cir::LoadOp>(retOp.getInput()[0].getDefiningOp());
1132 retAlloca = mlir::cast<cir::AllocaOp>(retLoad.getAddr().getDefiningOp());
1133 retAlloca.getResult().replaceAllUsesWith(sretPtr);
1139 builder.setInsertionPoint(retOp);
1140 cir::ReturnOp::create(builder, retOp.getLoc());
1142 if (retLoad.use_empty())
1161 builder.getNamedAttr(
"llvm.sret", mlir::TypeAttr::get(retTy)));
1163 builder.getNamedAttr(
"llvm.align", builder.getI64IntegerAttr(align)));
1166 builder.getNamedAttr(
"llvm.noalias", builder.getUnitAttr()));
1167 attrs.push_back(builder.getNamedAttr(
"llvm.writable", builder.getUnitAttr()));
1169 builder.getNamedAttr(
"llvm.dead_on_unwind", builder.getUnitAttr()));
1179void applySretSlotAttrs(cir::CallOp newCall, mlir::ArrayAttr argAttrs,
1180 mlir::Type retTy, uint64_t align,
1181 mlir::OpBuilder &builder) {
1182 mlir::MLIRContext *ctx = newCall->getContext();
1184 buildSretSlotAttrs(builder, retTy, align,
false);
1187 newArgAttrs.reserve(newCall.getArgOperands().size());
1188 newArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1190 llvm::append_range(newArgAttrs, argAttrs);
1191 assert(newArgAttrs.size() <= newCall.getArgOperands().size() &&
1192 "arg_attrs wider than the rewritten call's operand list");
1193 newArgAttrs.resize(newCall.getArgOperands().size(),
1194 mlir::DictionaryAttr::get(ctx));
1195 newCall->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, newArgAttrs));
1202static void copyCallAttributes(cir::CallOp source, cir::CallOp target) {
1203 source->getName().walkInherentAttrs(source, [&](llvm::StringRef name,
1204 mlir::Attribute &attr) {
1205 if (name != cir::CIRDialect::getCalleeAttrName())
1206 target->setInherentAttr(mlir::StringAttr::get(source->getContext(), name),
1209 for (mlir::NamedAttribute attr : source->getDiscardableAttrs())
1210 if (!target->hasDiscardableAttr(
attr.getName()))
1211 target->setDiscardableAttr(
attr.getName(),
attr.getValue());
1222static void prependIndirectCallee(cir::CallOp call,
1223 SmallVectorImpl<mlir::Value> &args,
1224 mlir::Type retTy,
unsigned numEllipsisArgs,
1225 mlir::OpBuilder &builder) {
1226 if (!call.isIndirect())
1228 assert(numEllipsisArgs <= args.size() &&
1229 "more ellipsis arguments than arguments");
1230 mlir::Value calleePtr = call.getIndirectCall();
1232 paramTypes.reserve(args.size() - numEllipsisArgs);
1233 llvm::transform(
ArrayRef(args).drop_back(numEllipsisArgs),
1234 std::back_inserter(paramTypes),
1235 [](mlir::Value v) {
return v.getType(); });
1245 assert((calleeFnTy.isVarArg() || numEllipsisArgs == 0) &&
1246 "only a variadic callee takes arguments through an ellipsis");
1247 auto newPtrTy = cir::PointerType::get(
1248 cir::FuncType::get(paramTypes, retTy, calleeFnTy.isVarArg()));
1249 if (calleePtr.getType() != newPtrTy)
1250 calleePtr = cir::CastOp::create(builder, call.getLoc(), newPtrTy,
1251 cir::CastKind::bitcast, calleePtr);
1252 args.insert(args.begin(), calleePtr);
1263void rewriteIndirectReturnCall(cir::CallOp call,
1264 const FunctionClassification &fc,
1266 unsigned numEllipsisArgs, mlir::Type origRetTy,
1268 mlir::OpBuilder &builder,
1269 const mlir::DataLayout &dl) {
1270 mlir::MLIRContext *ctx = call->getContext();
1271 auto ptrTy = cir::PointerType::get(origRetTy);
1272 builder.setInsertionPoint(call);
1273 uint64_t sretAlign = fc.returnInfo.indirectAlign.value();
1286 mlir::Value sretSlot =
nullptr;
1287 cir::StoreOp reuseStore =
nullptr;
1288 if (call.getResult().hasOneUse()) {
1289 mlir::Operation *user = *call.getResult().getUsers().begin();
1290 if (
auto store = mlir::dyn_cast<cir::StoreOp>(user))
1291 if (store.getValue() == call.getResult() &&
1292 store.getAddr().getType() == ptrTy &&
1293 mlir::DominanceInfo().properlyDominates(store.getAddr(), call)) {
1294 sretSlot = store.getAddr();
1299 auto alloca = cir::AllocaOp::create(
1300 builder, call.getLoc(), ptrTy,
1301 builder.getStringAttr(
"sret"),
1302 builder.getI64IntegerAttr(sretAlign));
1307 sretArgs.push_back(sretSlot);
1308 sretArgs.append(newArgs.begin(), newArgs.end());
1312 mlir::Type sretVoidTy = cir::VoidType::get(ctx);
1313 prependIndirectCallee(call, sretArgs, sretVoidTy, numEllipsisArgs, builder);
1314 auto newCall = cir::CallOp::create(
1315 builder, call.getLoc(), call.getCalleeAttr(), sretVoidTy, sretArgs);
1316 copyCallAttributes(call, newCall);
1317 newCall->removeAttr(
"res_attrs");
1322 mlir::ArrayAttr argAttrs = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1323 if (needsArgAttrUpdate(fc))
1324 argAttrs = updateArgAttrs(ctx, origCallArgTypes, argAttrs, fc, dl);
1325 applySretSlotAttrs(newCall, argAttrs, origRetTy, sretAlign, builder);
1331 reuseStore->erase();
1333 builder.setInsertionPointAfter(newCall);
1334 auto load = cir::LoadOp::create(builder, call.getLoc(), origRetTy, sretSlot,
1338 mlir::IntegerAttr(),
1339 cir::SyncScopeKindAttr(),
1340 cir::MemOrderAttr(),
1342 call.getResult().replaceAllUsesWith(load);
1349bool isSSERegisterClass(mlir::Type ty) {
1350 return mlir::isa<cir::VectorType, cir::FPTypeInterface>(ty);
1356 assert(call.isIndirect() &&
"expected an indirect call");
1357 return cast<cir::FuncType>(
1358 cast<cir::PointerType>(call.getIndirectCall().getType()).getPointee());
1367 cir::FuncOp funcOp,
unsigned argNo,
const ArgClassification &ac,
1368 mlir::BlockArgument blockArg, mlir::DominanceInfo &dom,
1369 SmallPtrSetImpl<mlir::Operation *> &claimedSlots) {
1375 cir::StoreOp extraSpill;
1376 mlir::Operation *otherUse =
nullptr;
1378 for (mlir::OpOperand &use : blockArg.getUses()) {
1379 auto store = dyn_cast<cir::StoreOp>(use.getOwner());
1380 if (store && store.getValue() == blockArg) {
1394 auto call = dyn_cast<cir::CIRCallOpInterface>(use.getOwner());
1395 if (call && llvm::is_contained(call.getArgOperands(), blockArg))
1396 callArgs.push_back(&use);
1398 otherUse = use.getOwner();
1402 return extraSpill->emitOpError()
1403 <<
"non-byval parameter " << argNo
1404 <<
" spilled more than once is not yet implemented in "
1408 return otherUse->emitOpError()
1409 <<
"non-byval parameter " << argNo
1410 <<
" consumed other than as a call argument is not yet implemented "
1411 "in CallConvLowering";
1416 if (callArgs.empty())
1417 return mlir::success();
1424 mlir::OpBuilder builder(funcOp.getContext());
1425 builder.setInsertionPointToStart(blockArg.getOwner());
1426 auto synthesized = cir::AllocaOp::create(
1427 builder, funcOp.getLoc(), cir::PointerType::get(blockArg.getType()),
1428 builder.getStringAttr(
"nonbyval.param"),
1429 builder.getI64IntegerAttr(ac.indirectAlign.value()));
1431 cir::StoreOp::create(builder, funcOp.getLoc(), blockArg, synthesized);
1439 cir::AllocaOp slot = spill.getAddr().getDefiningOp<cir::AllocaOp>();
1441 spill.getAddr().getType() != cir::PointerType::get(blockArg.getType()))
1442 return spill->emitOpError()
1443 <<
"non-byval parameter " << argNo
1444 <<
" spilled to storage that cannot take the incoming pointer is "
1445 "not yet implemented in CallConvLowering";
1451 if (!claimedSlots.insert(slot).second)
1452 return spill->emitOpError()
1453 <<
"non-byval parameter " << argNo
1454 <<
" sharing its spill slot with another parameter is not yet "
1455 "implemented in CallConvLowering";
1460 slot.setAlignment(ac.indirectAlign.value());
1462 if (callArgs.empty())
1463 return mlir::success();
1467 for (mlir::OpOperand *callArg : callArgs)
1468 if (!dom.properlyDominates(spill, callArg->getOwner()))
1469 return callArg->getOwner()->emitOpError()
1470 <<
"non-byval parameter " << argNo
1471 <<
" read before its spill is not yet implemented in "
1484 mlir::OpBuilder builder(spill);
1485 builder.setInsertionPointAfter(spill);
1486 auto reload = cir::LoadOp::create(builder, spill.getLoc(), slot);
1487 reload.setAlignment(ac.indirectAlign.value());
1488 for (mlir::OpOperand *callArg : callArgs)
1489 callArg->set(reload.getResult());
1490 return mlir::success();
1494 cir::FuncOp funcOp,
const FunctionClassification &fc) {
1495 if (!funcOp.isDefinition())
1496 return mlir::success();
1497 mlir::Region &body = funcOp->getRegion(0);
1499 return mlir::success();
1500 mlir::Block &entry = body.front();
1501 mlir::DominanceInfo dom;
1502 SmallPtrSet<mlir::Operation *, 4> claimedSlots;
1508 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1509 if (ac.kind != ArgKind::Indirect || ac.byVal)
1511 assert(idx < entry.getNumArguments() &&
1512 "classification count must not exceed entry block arguments");
1514 funcOp, idx, ac, entry.getArgument(idx), dom, claimedSlots)))
1515 return mlir::failure();
1517 return mlir::success();
1521 for (
auto [slot, incoming] : pendingParamSlots) {
1522 slot.getResult().replaceAllUsesWith(incoming);
1525 pendingParamSlots.clear();
1529 mlir::FunctionOpInterface funcOpInterface,
const FunctionClassification &fc,
1530 mlir::OpBuilder &builder) {
1536 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(funcOpInterface);
1538 if (!fc.needsRewrite())
1539 return mlir::success();
1543 mlir::MLIRContext *ctx = funcOp->getContext();
1548 assert(oldResultTypes.size() <= 1 &&
1549 "CIR functions return zero or one value");
1552 if (mlir::failed(buildNewArgTypes(oldArgTypes, fc, newArgTypes,
1553 [&]() {
return funcOp.emitOpError(); })))
1554 return mlir::failure();
1556 mlir::Type voidTy = cir::VoidType::get(ctx);
1557 mlir::Type origRetTy = oldResultTypes.empty() ? voidTy : oldResultTypes[0];
1558 mlir::Type newRetTy = computeNewReturnType(
1559 origRetTy, fc.returnInfo, ctx, [&]() { return funcOp.emitOpError(); });
1561 return mlir::failure();
1571 fc.returnInfo.kind == ArgKind::Indirect && !oldResultTypes.empty();
1573 newArgTypes.insert(newArgTypes.begin(), cir::PointerType::get(origRetTy));
1575 if (funcOp.isDefinition()) {
1576 mlir::Region &body = funcOp->getRegion(0);
1577 if (!body.empty()) {
1582 body.front().insertArgument(0u, cir::PointerType::get(origRetTy),
1584 insertSRetStores(funcOp, origRetTy, builder);
1593 insertArgCoercion(funcOp, fc, builder, dl, hasSRet, pendingParamSlots);
1598 if (fc.returnInfo.kind == ArgKind::Direct && fc.returnInfo.coercedType &&
1599 !oldResultTypes.empty() && fc.returnInfo.coercedType != origRetTy)
1600 insertReturnCoercion(funcOp, origRetTy, fc.returnInfo.coercedType,
1601 builder, dl, fc.returnInfo.directOffset);
1603 mlir::Block &entry = body.front();
1612 unsigned blockArgIdx = hasSRet ? 1 : 0;
1613 for (
auto [i, ac] : llvm::enumerate(fc.argInfos)) {
1614 if (blockArgIdx >= entry.getNumArguments())
1616 if (ac.kind == ArgKind::Ignore) {
1617 mlir::BlockArgument arg = entry.getArgument(blockArgIdx);
1618 if (!arg.use_empty()) {
1619 builder.setInsertionPointToStart(&entry);
1620 mlir::Value poison =
1621 createIgnoredValue(builder, funcOp.getLoc(), arg.getType());
1622 arg.replaceAllUsesWith(poison);
1624 entry.eraseArgument(blockArgIdx);
1628 blockArgIdx += flatTy.getNumElements();
1629 else if (ac.kind == ArgKind::Expand)
1630 blockArgIdx += cast<cir::RecordType>(oldArgTypes[i]).getNumElements();
1642 if (fc.returnInfo.kind == ArgKind::Ignore && !oldResultTypes.empty()) {
1643 assert(mlir::isa<cir::VoidType>(newRetTy) &&
1644 "Ignore-return path requires the new return type to be void");
1646 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
1647 for (cir::ReturnOp r : returns) {
1648 if (r.getNumOperands() == 0)
1650 builder.setInsertionPoint(r);
1651 cir::ReturnOp::create(builder, r.getLoc());
1657 mlir::Type newFnTy = funcOp.cloneTypeWith(newArgTypes, newResultTypes);
1658 funcOp.setFunctionTypeAttr(mlir::TypeAttr::get(newFnTy));
1662 if (hasSRet || needsArgAttrUpdate(fc)) {
1663 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1664 mlir::ArrayAttr updated =
1665 updateArgAttrs(ctx, oldArgTypes, existing, fc, dl);
1671 builder, origRetTy, fc.returnInfo.indirectAlign.value(),
1672 funcOp.isDefinition());
1674 withSret.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1675 llvm::append_range(withSret, updated);
1676 funcOp->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, withSret));
1678 funcOp->setAttr(
"arg_attrs", updated);
1682 if (mlir::isa<cir::VoidType>(newRetTy)) {
1683 funcOp->removeAttr(
"res_attrs");
1684 }
else if (fc.returnInfo.kind == ArgKind::Extend ||
1685 fc.returnInfo.kind == ArgKind::Direct) {
1686 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1687 if (mlir::ArrayAttr updated =
1688 updateResAttrs(ctx, existing, fc.returnInfo, origRetTy, dl))
1689 funcOp->setAttr(
"res_attrs", updated);
1692 return mlir::success();
1697 const FunctionClassification &fc,
1698 mlir::OpBuilder &builder) {
1710 unsigned numOperands =
1711 mlir::cast<cir::CIRCallOpInterface>(callOp).getNumArgOperands();
1712 if (numOperands > fc.argInfos.size())
1713 return callOp->emitOpError()
1714 <<
"variadic arguments not yet implemented in CallConvLowering";
1715 if (numOperands < fc.argInfos.size())
1716 return callOp->emitOpError()
1717 <<
"call passes fewer arguments than the callee declares, which is "
1718 "not yet implemented in CallConvLowering";
1720 if (!fc.needsRewrite())
1721 return mlir::success();
1723 if (mlir::isa<cir::TryCallOp>(callOp))
1724 return callOp->emitOpError()
1725 <<
"TryCallOp not yet implemented in CallConvLowering";
1727 auto call = mlir::cast<cir::CallOp>(callOp);
1728 mlir::MLIRContext *ctx = callOp->getContext();
1729 mlir::Block *slotBlock = coercionSlotBlock(call);
1731 builder.setInsertionPoint(call);
1734 mlir::ValueRange argOperands = call.getArgOperands();
1735 newArgs.reserve(argOperands.size());
1747 llvm::append_range(origCallArgTypes, argOperands.getTypes());
1753 unsigned numDeclared = fc.argInfos.size();
1754 if (call.isIndirect()) {
1756 assert(numDeclared <= fc.argInfos.size() &&
1757 "the classification covers at least the declared parameters");
1761 std::optional<unsigned> ellipsisStart;
1762 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1763 if (idx == numDeclared)
1764 ellipsisStart = newArgs.size();
1765 if (ac.kind == ArgKind::Ignore)
1767 mlir::Value arg = argOperands[idx];
1775 if (arg.getType() != flatTy) {
1776 SmallPtrSet<mlir::Operation *, 4> coercionOps;
1777 assert(!ac.directOffset &&
1778 "each field is read from slot offset 0 here, so a flattened "
1779 "coercion cannot honor a direct offset");
1780 mlir::Value coercedPtr =
1781 emitCoercionToMemory(builder, call.getLoc(), flatTy, arg, slotBlock,
1782 dl, coercionOps, 0);
1783 auto coercedPtrTy = mlir::cast<cir::PointerType>(coercedPtr.getType());
1784 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
1785 mlir::Type fieldPtrTy =
1786 cir::PointerType::get(fieldTy, coercedPtrTy.getAddrSpace());
1788 cir::GetMemberOp::create(builder, call.getLoc(), fieldPtrTy,
1790 newArgs.push_back(cir::LoadOp::create(builder, call.getLoc(), fieldTy,
1791 fieldPtr.getResult()));
1794 emitStructFieldArgs(builder, call.getLoc(), arg, flatTy, newArgs,
1797 }
else if (ac.kind == ArgKind::Expand) {
1800 auto recTy = cast<cir::RecordType>(arg.getType());
1802 "Expand classification requires a struct type, not a union");
1803 emitStructFieldArgs(builder, call.getLoc(), arg, recTy, newArgs,
1805 }
else if (ac.kind == ArgKind::Direct && ac.coercedType &&
1806 arg.getType() != ac.coercedType) {
1807 arg = emitCoercion(builder, call.getLoc(), ac.coercedType, arg, slotBlock,
1808 dl, ac.directOffset);
1809 newArgs.push_back(arg);
1810 }
else if (ac.kind == ArgKind::Indirect) {
1811 uint64_t minAlign = ac.indirectAlign.value();
1819 cir::LoadOp srcLoad;
1820 if (!ac.byVal || mlir::isa<cir::RecordType>(arg.getType()))
1821 srcLoad = maybeGetSimpleLoad(arg);
1829 cir::LoadOp fwdLoad = forwardableIndirectLoad(srcLoad, minAlign);
1831 return call->emitOpError()
1832 <<
"non-byval indirect argument that does not name the "
1833 "caller's storage is not yet implemented in "
1835 newArgs.push_back(fwdLoad.getAddr());
1836 deadRecordLoads.push_back(fwdLoad);
1845 if (cir::LoadOp fwdLoad = forwardableIndirectLoad(srcLoad, minAlign);
1846 fwdLoad && storageUnwrittenBetween(fwdLoad, call.getOperation(),
1848 newArgs.push_back(fwdLoad.getAddr());
1849 deadRecordLoads.push_back(fwdLoad);
1855 auto ptrTy = cir::PointerType::get(arg.getType());
1856 mlir::IntegerAttr slotAlign = builder.getI64IntegerAttr(minAlign);
1857 mlir::OpBuilder::InsertionGuard guard(builder);
1859 builder.setInsertionPoint(srcLoad);
1862 cir::AllocaOp::create(builder, call.getLoc(), ptrTy,
1863 builder.getStringAttr(
"byval"), slotAlign);
1865 cir::CopyOp::create(builder, call.getLoc(), slot, srcLoad.getAddr(),
1866 slotAlign, loadSourceAlignment(srcLoad, builder));
1867 deadRecordLoads.push_back(srcLoad);
1869 cir::StoreOp::create(builder, call.getLoc(), arg, slot);
1871 newArgs.push_back(slot);
1873 newArgs.push_back(arg);
1876 unsigned numEllipsisArgs =
1877 ellipsisStart ? newArgs.size() - *ellipsisStart : 0;
1879 bool hasResult = call.getNumResults() > 0;
1880 mlir::Type origRetTy =
1881 hasResult ? call.getResult().getType() : cir::VoidType::get(ctx);
1887 if (fc.returnInfo.kind == ArgKind::Indirect && hasResult) {
1888 rewriteIndirectReturnCall(call, fc, newArgs, numEllipsisArgs, origRetTy,
1889 origCallArgTypes, builder, dl);
1890 eraseDeadRecordLoads(deadRecordLoads);
1891 return mlir::success();
1894 mlir::Type callRetTy = origRetTy;
1895 if (fc.returnInfo.kind == ArgKind::Ignore && hasResult)
1896 callRetTy = cir::VoidType::get(ctx);
1897 bool returnNeedsCoercion =
1898 hasResult && fc.returnInfo.kind == ArgKind::Direct &&
1899 fc.returnInfo.coercedType && fc.returnInfo.coercedType != origRetTy;
1900 if (returnNeedsCoercion)
1901 callRetTy = fc.returnInfo.coercedType;
1903 builder.setInsertionPoint(call);
1904 prependIndirectCallee(call, newArgs, callRetTy, numEllipsisArgs, builder);
1905 auto newCall = cir::CallOp::create(builder, call.getLoc(),
1906 call.getCalleeAttr(), callRetTy, newArgs);
1907 copyCallAttributes(call, newCall);
1911 if (returnNeedsCoercion) {
1912 builder.setInsertionPointAfter(newCall);
1913 mlir::Value coercedBack =
1914 emitCoercion(builder, call.getLoc(), origRetTy, newCall.getResult(),
1915 slotBlock, dl, fc.returnInfo.directOffset);
1916 call.getResult().replaceAllUsesWith(coercedBack);
1919 if (needsArgAttrUpdate(fc)) {
1920 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1921 newCall->setAttr(
"arg_attrs",
1922 updateArgAttrs(ctx, origCallArgTypes, existing, fc, dl));
1926 if (fc.returnInfo.kind == ArgKind::Extend ||
1927 (hasResult && fc.returnInfo.kind == ArgKind::Direct)) {
1928 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1929 if (mlir::ArrayAttr updated =
1930 updateResAttrs(ctx, existing, fc.returnInfo, origRetTy, dl))
1931 newCall->setAttr(
"res_attrs", updated);
1932 }
else if (hasResult && mlir::isa<cir::VoidType>(callRetTy)) {
1933 newCall->removeAttr(
"res_attrs");
1936 if (hasResult && fc.returnInfo.kind == ArgKind::Ignore) {
1941 if (!call.getResult().use_empty()) {
1942 builder.setInsertionPointAfter(newCall);
1943 mlir::Value poison =
1944 createIgnoredValue(builder, call.getLoc(), origRetTy);
1945 call.getResult().replaceAllUsesWith(poison);
1947 }
else if (hasResult && !returnNeedsCoercion) {
1949 call.getResult().replaceAllUsesWith(newCall.getResult());
1953 eraseDeadRecordLoads(deadRecordLoads);
1955 return mlir::success();
1960 mlir::OpBuilder &builder) {
1961 auto oldPtrTy = mlir::cast<cir::PointerType>(addrOp.getAddr().getType());
1962 cir::FuncType newFuncTy = funcOp.getFunctionType();
1965 if (newFuncTy == oldPtrTy.getPointee())
1969 addrOp.getAddr().setType(cir::PointerType::get(newFuncTy));
1970 if (addrOp.getAddr().use_empty())
1975 mlir::OpBuilder::InsertionGuard guard(builder);
1976 builder.setInsertionPointAfter(addrOp);
1977 auto bitcast = cir::CastOp::create(builder, addrOp.getLoc(), oldPtrTy,
1978 cir::CastKind::bitcast, addrOp.getAddr());
1979 addrOp.getAddr().replaceAllUsesExcept(bitcast.getResult(), bitcast);
1988 VAArgFetch(mlir::Location loc, mlir::Value valist,
1990 const ArgClassification &ac,
const mlir::DataLayout &dl,
1991 mlir::ModuleOp module)
1992 : loc(loc), valist(valist), vaFields(vaFields), resultTy(resultTy),
1993 ac(ac), dl(dl), module(module) {
1994 assert(vaFields.size() == 4 &&
1995 "the x86-64 va_list is a four-field cursor, checked by the caller");
2001 mlir::Type resultTy;
2002 const ArgClassification ∾
2003 const mlir::DataLayout &dl;
2004 mlir::ModuleOp module;
2010struct RegisterCursor {
2011 mlir::Value gpOffsetP;
2012 mlir::Value fpOffsetP;
2013 mlir::Value gpOffset;
2014 mlir::Value fpOffset;
2018mlir::LogicalResult reportVAArgNYI(cir::VAArgOp op, llvm::StringRef what) {
2019 op->emitOpError() <<
"va_arg of " << what
2020 <<
" not yet implemented in CallConvLowering";
2021 return mlir::failure();
2028mlir::LogicalResult classifyRegisterPair(cir::VAArgOp op,
2029 const ArgClassification &ac,
2030 std::array<bool, 2> &pairIsSse,
2032 auto pairTy = mlir::dyn_cast<cir::RecordType>(ac.coercedType);
2034 return mlir::success();
2036 if (pairTy.getNumElements() != 2)
2037 return reportVAArgNYI(op,
"a register coercion that is not two eightbytes");
2039 assert(!ac.directOffset &&
2040 "a pair already spans both eightbytes, so it cannot also start "
2041 "partway into the value");
2042 for (
auto [i, memberTy] : llvm::enumerate(pairTy.getMembers()))
2043 pairIsSse[i] = isSSERegisterClass(memberTy);
2045 return mlir::success();
2053uint64_t argumentAreaAlign(mlir::Type ty, mlir::ModuleOp modOp,
2054 const mlir::DataLayout &dl) {
2056 if (
auto recTy = mlir::dyn_cast<cir::RecordType>(ty))
2058 align = std::max<uint64_t>(align, layout.getRecordAlign());
2062mlir::Value roundPointerUpToAlignment(
CIRBaseBuilderTy &b, mlir::Location loc,
2063 mlir::Value bytePtr, uint64_t align,
2064 const mlir::DataLayout &dl) {
2065 assert(mlir::cast<cir::PointerType>(bytePtr.getType()).getPointee() ==
2067 "the bump strides in bytes, so the pointee must be u8");
2068 assert(llvm::isPowerOf2_64(align) &&
2069 "mask rounding needs a power-of-two alignment");
2070 mlir::Value bumped =
2072 std::optional<uint64_t> indexWidth =
2073 dl.getTypeIndexBitwidth(bytePtr.getType());
2074 assert(indexWidth &&
"a pointer in the argument area has an index width");
2076 static_cast<unsigned>(*indexWidth));
2077 return cir::PtrMaskOp::create(b, loc, bytePtr.getType(), bumped, mask);
2083 const VAArgFetch &f) {
2086 f.loc, b.
getPointerTo(f.vaFields[2]), f.valist,
"overflow_arg_area", 2);
2087 mlir::Value overflow = b.
createLoad(f.loc, overflowP);
2090 uint64_t tyAlign = argumentAreaAlign(f.resultTy, f.module, f.dl);
2092 bytePtr = roundPointerUpToAlignment(b, f.loc, bytePtr, tyAlign, f.dl);
2094 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
2095 uint64_t stride = (tySize + 7) & ~UINT64_C(7);
2096 mlir::Value strideVal = b.
getSignedInt(f.loc, stride, 32);
2107 unsigned neededInt,
unsigned neededSse) {
2108 RegisterCursor cursor;
2111 f.valist,
"gp_offset", 0);
2112 cursor.gpOffset = b.
createLoad(f.loc, cursor.gpOffsetP);
2114 b.
getConstantInt(f.loc, cursor.gpOffset.getType(), 48 - neededInt * 8);
2116 b.
createCompare(f.loc, cir::CmpOpKind::le, cursor.gpOffset, limit);
2120 f.valist,
"fp_offset", 1);
2121 cursor.fpOffset = b.
createLoad(f.loc, cursor.fpOffsetP);
2122 mlir::Value limit = b.
getConstantInt(f.loc, cursor.fpOffset.getType(),
2123 176 - neededSse * 16);
2124 mlir::Value fitsInFp =
2125 b.
createCompare(f.loc, cir::CmpOpKind::le, cursor.fpOffset, limit);
2126 cursor.inRegs = cursor.inRegs
2136 const ArgClassification &ac,
2137 const RegisterCursor &cursor,
2138 const std::array<bool, 2> &pairIsSse,
2139 mlir::Value regSaveArea, mlir::Value regPairTemp) {
2140 auto pairTy = mlir::cast<cir::RecordType>(ac.coercedType);
2144 bool bothSse = pairIsSse[0] && pairIsSse[1];
2147 unsigned seenOfClass[2] = {0, 0};
2148 for (
unsigned i = 0; i < 2; ++i) {
2149 bool isSse = pairIsSse[i];
2150 mlir::Value base = isSse ? cursor.fpOffset : cursor.gpOffset;
2151 unsigned regSize = isSse ? 16 : 8;
2152 unsigned prior = seenOfClass[isSse];
2153 ++seenOfClass[isSse];
2154 mlir::Value off = base;
2160 mlir::Type elemTy = pairTy.getElementType(i);
2171uint64_t registerSlotSize(
unsigned neededInt,
unsigned neededSse) {
2172 assert(!(neededInt && neededSse) &&
2173 "a fetch needing both classes is a pair, reassembled elsewhere");
2174 return neededSse ? neededSse * 16 : neededInt * 8;
2179bool needsTempCopy(
const VAArgFetch &f,
unsigned neededInt,
2180 unsigned neededSse) {
2181 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
2182 if (f.ac.coercedType &&
2183 (f.ac.directOffset || registerSlotSize(neededInt, neededSse) < tySize))
2187 uint64_t slotAlign = neededSse ? 16 : 8;
2188 return argumentAreaAlign(f.resultTy, f.module, f.dl) > slotAlign;
2194 const VAArgFetch &f, mlir::Value regAddr,
2195 mlir::Value temp,
unsigned neededInt,
2196 unsigned neededSse) {
2198 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
2200 if (f.ac.coercedType &&
2201 (f.ac.directOffset || registerSlotSize(neededInt, neededSse) < tySize)) {
2208 mlir::Value dst = temp;
2209 if (f.ac.directOffset) {
2211 mlir::Value offset = b.
getSignedInt(loc, f.ac.directOffset, 32);
2225 const RegisterCursor &cursor,
unsigned neededInt,
2226 unsigned neededSse) {
2247 const ArgClassification &ac,
2248 mlir::OpBuilder &opBuilder) {
2249 auto op = mlir::cast<cir::VAArgOp>(vaArgOp);
2251 mlir::Location loc = op.getLoc();
2252 mlir::Type resultTy = op.getType();
2253 mlir::Value valist = op.getArgList();
2258 if (ac.kind == ArgKind::Ignore) {
2259 builder.setInsertionPoint(op);
2260 op.getResult().replaceAllUsesWith(
2261 createIgnoredValue(builder, loc, resultTy));
2263 return mlir::success();
2266 if (ac.kind == ArgKind::Indirect && !ac.byVal)
2267 return reportVAArgNYI(op,
"a non-trivially-copyable type");
2272 unsigned neededInt = ac.neededIntRegs;
2273 unsigned neededSse = ac.neededSseRegs;
2277 std::array<bool, 2> pairIsSse = {
false,
false};
2278 bool isRegPair =
false;
2279 if (ac.kind == ArgKind::Direct && neededInt + neededSse == 2 &&
2281 if (classifyRegisterPair(op, ac, pairIsSse, isRegPair).failed())
2282 return mlir::failure();
2285 auto vaListRecTy = mlir::dyn_cast<cir::RecordType>(
2286 mlir::cast<cir::PointerType>(valist.getType()).getPointee());
2287 if (!vaListRecTy || vaListRecTy.getNumElements() != 4) {
2288 return reportVAArgNYI(op,
2289 "a va_list that is not the four-field gp_offset / "
2290 "fp_offset / overflow_arg_area / reg_save_area "
2294 const VAArgFetch fetch{loc, valist, vaListRecTy.getMembers(), resultTy, ac,
2298 builder.setInsertionPoint(op);
2301 if (neededInt == 0 && neededSse == 0) {
2302 addr = buildOverflowAddrAndAdvance(builder, fetch);
2304 RegisterCursor cursor =
2305 buildRegisterGate(builder, fetch, neededInt, neededSse);
2313 bool pairNeedsReassembly = isRegPair && neededSse != 0;
2316 mlir::Value regPairTemp;
2317 if (pairNeedsReassembly) {
2322 loc, builder.
getPointerTo(ac.coercedType),
"vaarg.reg",
2324 std::max(dl.getTypeABIAlignment(ac.coercedType),
2325 argumentAreaAlign(resultTy, module, dl))));
2328 mlir::Value regTemp;
2329 bool copyThroughTemp =
2330 !pairNeedsReassembly && needsTempCopy(fetch, neededInt, neededSse);
2331 if (copyThroughTemp) {
2335 argumentAreaAlign(resultTy, module, dl)));
2338 addr = cir::TernaryOp::create(
2339 builder, loc, cursor.inRegs,
2341 [&](mlir::OpBuilder &ob, mlir::Location l) {
2342 CIRBaseBuilderTy b(ob);
2343 mlir::Value regSaveArea = b.createLoad(
2344 l, b.createGetMember(l, b.getPointerTo(fetch.vaFields[3]),
2345 valist,
"reg_save_area", 3));
2346 regSaveArea = b.createPtrBitcast(regSaveArea, byteTy);
2348 mlir::Value regAddr;
2349 if (pairNeedsReassembly) {
2350 reassembleRegisterPair(b, l, ac, cursor, pairIsSse,
2351 regSaveArea, regPairTemp);
2352 regAddr = b.createPtrBitcast(regPairTemp, byteTy);
2355 neededSse ? cursor.fpOffset : cursor.gpOffset;
2356 regAddr = b.createPtrStride(l, regSaveArea, off);
2357 if (copyThroughTemp) {
2358 regAddr = copyRegisterToTemp(b, l, fetch, regAddr, regTemp,
2359 neededInt, neededSse);
2363 advanceRegisterCursors(b, l, cursor, neededInt, neededSse);
2364 cir::YieldOp::create(b, l, regAddr);
2367 [&](mlir::OpBuilder &ob, mlir::Location l) {
2369 mlir::Value memAddr = buildOverflowAddrAndAdvance(b, fetch);
2370 cir::YieldOp::create(b, l, memAddr);
2376 mlir::Value result =
2377 builder.createAlignedLoad(loc, builder.createPtrBitcast(addr, resultTy),
2378 argumentAreaAlign(resultTy, module, dl));
2379 op.getResult().replaceAllUsesWith(result);
2381 return mlir::success();
static mlir::LogicalResult prepareOneNonByvalParameter(cir::FuncOp funcOp, unsigned argNo, const ArgClassification &ac, mlir::BlockArgument blockArg, mlir::DominanceInfo &dom, SmallPtrSetImpl< mlir::Operation * > &claimedSlots)
Bring funcOp's non-byval indirect parameter argNo into the shape the rest of the rewrite assumes.
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
void finalizeParameterSlots()
Replace each non-byval indirect parameter's spill slot with the incoming pointer, so the body operate...
mlir::LogicalResult prepareNonByvalParameters(cir::FuncOp funcOp, const mlir::abi::FunctionClassification &fc)
Bring each non-byval indirect parameter of funcOp into the shape the rest of the rewrite assumes: the...
mlir::LogicalResult rewriteVAArg(mlir::Operation *vaArgOp, const mlir::abi::ArgClassification &ac, mlir::OpBuilder &builder) override
Expand a cir.va_arg into the x86-64 SysV register-save-area / overflow-area sequence.
mlir::LogicalResult rewriteCallSite(mlir::Operation *callOp, const mlir::abi::FunctionClassification &fc, mlir::OpBuilder &builder) override
cir::PtrStrideOp createPtrStride(mlir::Location loc, mlir::Value base, mlir::Value stride)
mlir::Value createAdd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs, OverflowBehavior ob=OverflowBehavior::None)
cir::GetMemberOp createGetMember(mlir::Location loc, mlir::Type resultTy, mlir::Value base, llvm::StringRef name, unsigned index)
cir::PointerType getPointerTo(mlir::Type ty)
cir::IntType getUIntNTy(int n)
mlir::Value createPtrBitcast(mlir::Value src, mlir::Type newPointeeTy)
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType, llvm::StringRef name, mlir::IntegerAttr alignment, mlir::Value dynAllocSize)
cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr, bool isVolatile=false, uint64_t alignment=0, bool isNontemporal=false)
mlir::Value getSignedInt(mlir::Location loc, int64_t val, unsigned numBits)
cir::CmpOp createCompare(mlir::Location loc, cir::CmpOpKind kind, mlir::Value lhs, mlir::Value rhs)
cir::ConstantOp getConstantInt(mlir::Location loc, mlir::Type ty, int64_t value)
mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr, uint64_t alignment)
mlir::Value createLogicalAnd(mlir::Location loc, mlir::Value lhs, mlir::Value rhs)
cir::StoreOp createStore(mlir::Location loc, mlir::Value val, mlir::Value dst, bool isVolatile=false, bool isNontemporal=false, mlir::IntegerAttr align={}, cir::SyncScopeKindAttr scope={}, cir::MemOrderAttr order={})
llvm::TypeSize getTypeAllocSize(mlir::Type ty) const
Returns the offset in bytes between successive objects of the specified type, including alignment pad...
llvm::TypeSize getTypeStoreSize(mlir::Type ty) const
Returns the maximum number of bytes that may be overwritten by storing the specified type.
C++ view class that accepts both !cir.struct and !cir.union types.
llvm::ArrayRef< mlir::Type > getMembers() const
mlir::StringAttr getName() const
size_t getNumElements() const
static CharUnits fromQuantity(QuantityType Quantity)
Construct a CharUnits quantity from a raw integer type.
RecordLayoutAttr tryGetRecordLayout(mlir::ModuleOp mod, mlir::StringAttr name)
Same lookup as getRecordLayout, but returns a null attribute instead of asserting when the record has...
cir::FuncType getIndirectCalleeType(cir::CIRCallOpInterface call)
The signature the indirect call call reaches its callee through.
AllocaOp getUnderlyingAlloca(mlir::Value addr)
The alloca that defines addr, looking through casts that preserve the underlying storage.
const internal::VariadicAllOfMatcher< Attr > attr
constexpr size_t align(size_t Size)
Aligns a size to the pointer alignment.
static bool deadOnReturnAttr()
static bool noaliasOnByvalAttr()