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/IR/Attributes.h"
21#include "llvm/Support/MathExtras.h"
28using namespace mlir::abi;
76 if (ac.kind != ArgKind::Direct || !ac.coercedType || !ac.canFlatten)
78 auto recTy = dyn_cast<cir::RecordType>(ac.coercedType);
79 if (!recTy || !recTy.isStruct() || recTy.getNumElements() <= 1)
91 const FunctionClassification &fc,
92 SmallVectorImpl<mlir::Type> &newArgTypes,
93 function_ref<mlir::InFlightDiagnostic()> emitError) {
94 assert(newArgTypes.empty() &&
"expected an empty output vector");
95 newArgTypes.reserve(oldArgTypes.size());
96 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
97 mlir::Type origTy = oldArgTypes[idx];
105 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);
222mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx, mlir::ModuleOp module,
224 mlir::ArrayAttr existingArgAttrs,
225 const FunctionClassification &fc,
226 const mlir::DataLayout &dl) {
227 mlir::Builder builder(ctx);
229 newArgAttrs.reserve(fc.argInfos.size());
230 for (
auto [oldIdx, ac] : llvm::enumerate(fc.argInfos)) {
231 if (ac.kind == ArgKind::Ignore)
233 mlir::DictionaryAttr existing = builder.getDictionaryAttr({});
234 if (existingArgAttrs && oldIdx < existingArgAttrs.size())
235 existing = mlir::cast<mlir::DictionaryAttr>(existingArgAttrs[oldIdx]);
236 if (ac.kind == ArgKind::Direct && ac.coercedType &&
237 isCoercionWiderThanMemory(origArgTypes[oldIdx], ac.coercedType, dl)) {
238 mlir::NamedAttrList attrs(existing);
239 attrs.erase(mlir::LLVM::LLVMDialect::getNoUndefAttrName());
240 existing = attrs.getDictionary(ctx);
245 newArgAttrs.append(flatTy.getNumElements(), existing);
246 }
else if (ac.kind == ArgKind::Expand) {
249 auto recTy = cast<cir::RecordType>(origArgTypes[oldIdx]);
250 newArgAttrs.append(recTy.getNumElements(), builder.getDictionaryAttr({}));
251 }
else if (ac.kind == ArgKind::Extend) {
252 StringRef attrName = ac.signExtend
253 ? mlir::LLVM::LLVMDialect::getSExtAttrName()
254 :
mlir::LLVM::LLVMDialect::getZExtAttrName();
255 mlir::NamedAttrList attrs(existing);
256 attrs.set(attrName, builder.getUnitAttr());
257 newArgAttrs.push_back(attrs.getDictionary(ctx));
258 }
else if (ac.kind == ArgKind::Indirect) {
266 mlir::Type pointeeTy = origArgTypes[oldIdx];
267 mlir::NamedAttrList attrs(existing);
268 attrs.erase(mlir::LLVM::LLVMDialect::getNoFPClassAttrName());
269 attrs.set(mlir::LLVM::LLVMDialect::getAlignAttrName(),
270 builder.getI64IntegerAttr(ac.indirectAlign.value()));
271 attrs.set(mlir::LLVM::LLVMDialect::getNoUndefAttrName(),
272 builder.getUnitAttr());
278 attrs.set(mlir::LLVM::LLVMDialect::getByValAttrName(),
279 mlir::TypeAttr::get(pointeeTy));
283 attrs.set(mlir::LLVM::LLVMDialect::getNoFreeObjAttrName(),
284 builder.getUnitAttr());
288 auto recTy = mlir::dyn_cast<cir::RecordType>(pointeeTy);
289 cir::RecordLayoutAttr layout =
291 :
cir::RecordLayoutAttr();
292 if (!layout || layout.getHasTrivialDtor())
293 attrs.set(mlir::LLVM::LLVMDialect::getDeadOnReturnAttrName(),
294 builder.getI64IntegerAttr(
static_cast<int64_t>(
295 llvm::DeadOnReturnInfo().toIntValue())));
296 attrs.set(mlir::LLVM::LLVMDialect::getDereferenceableAttrName(),
297 builder.getI64IntegerAttr(
298 dl.getTypeSize(pointeeTy).getFixedValue()));
300 newArgAttrs.push_back(attrs.getDictionary(ctx));
302 newArgAttrs.push_back(existing);
305 return builder.getArrayAttr(newArgAttrs);
314mlir::ArrayAttr updateResAttrs(mlir::MLIRContext *ctx,
315 mlir::ArrayAttr existingResAttrs,
316 const ArgClassification &retInfo,
317 mlir::Type origRetTy,
318 const mlir::DataLayout &dl) {
319 bool needsNoUndefDrop =
320 retInfo.kind == ArgKind::Direct && retInfo.coercedType &&
321 isCoercionWiderThanMemory(origRetTy, retInfo.coercedType, dl);
322 if (retInfo.kind != ArgKind::Extend && !needsNoUndefDrop)
323 return existingResAttrs;
325 mlir::NamedAttrList attrs;
326 if (existingResAttrs && !existingResAttrs.empty())
328 mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0]).getValue());
329 if (retInfo.kind == ArgKind::Extend)
330 attrs.set(retInfo.signExtend ?
"llvm.signext" :
"llvm.zeroext",
331 mlir::UnitAttr::get(ctx));
332 if (needsNoUndefDrop)
333 attrs.erase(mlir::LLVM::LLVMDialect::getNoUndefAttrName());
334 return mlir::ArrayAttr::get(ctx, {attrs.getDictionary(ctx)});
347static uint64_t coercionByteSize(mlir::Type ty,
const mlir::DataLayout &dl) {
348 if (
auto intTy = mlir::dyn_cast<cir::IntType>(ty))
349 return llvm::divideCeil(intTy.getWidth(), 8);
350 return dl.getTypeSize(ty);
359emitViewAtOffset(mlir::OpBuilder &builder, mlir::Location loc,
360 mlir::Type wantTy, mlir::Value base,
361 SmallPtrSetImpl<mlir::Operation *> &createdOps,
363 auto basePtrTy = mlir::cast<cir::PointerType>(base.getType());
364 if (wantTy == basePtrTy.getPointee()) {
365 assert(offset == 0 &&
"a view of the storage's own type has no offset");
368 mlir::ptr::MemorySpaceAttrInterface addrSpace = basePtrTy.getAddrSpace();
369 mlir::Value view = base;
371 auto u8Ty = cir::IntType::get(builder.getContext(), 8,
false);
372 auto u8PtrTy = cir::PointerType::get(u8Ty, addrSpace);
373 auto u8Base = cir::CastOp::create(builder, loc, u8PtrTy,
374 cir::CastKind::bitcast, base);
375 createdOps.insert(u8Base);
377 cir::IntType::get(builder.getContext(), 64,
true);
378 auto strideVal = cir::ConstantOp::create(
379 builder, loc, cir::IntAttr::get(strideTy, offset));
380 createdOps.insert(strideVal);
381 view = cir::PtrStrideOp::create(builder, loc, u8PtrTy, u8Base, strideVal);
382 createdOps.insert(view.getDefiningOp());
384 auto typedView = cir::CastOp::create(builder, loc,
385 cir::PointerType::get(wantTy, addrSpace),
386 cir::CastKind::bitcast, view);
387 createdOps.insert(typedView);
393static cir::PointerType getAllocaPtrTy(mlir::Type pointee,
394 const mlir::DataLayout &dl) {
395 mlir::ptr::MemorySpaceAttrInterface addrSpace;
396 if (
auto asAttr = mlir::dyn_cast_if_present<mlir::IntegerAttr>(
397 dl.getAllocaMemorySpace()))
398 addrSpace = cir::TargetAddressSpaceAttr::get(pointee.getContext(),
400 return cir::PointerType::get(pointee, addrSpace);
408static cir::AllocaOp createCoercionSlot(
409 mlir::OpBuilder &builder, mlir::Location loc, mlir::Type srcTy,
410 mlir::Type dstTy, mlir::Block *slotBlock,
const mlir::DataLayout &dl,
411 SmallPtrSetImpl<mlir::Operation *> &createdOps,
unsigned offset) {
412 assert(srcTy != dstTy &&
"callers must pre-check that the types differ");
414 uint64_t srcAlign = dl.getTypeABIAlignment(srcTy);
415 uint64_t dstAlign = dl.getTypeABIAlignment(dstTy);
416 uint64_t allocaAlign = std::max(srcAlign, dstAlign);
417 mlir::Type slotTy = coercionByteSize(srcTy, dl) >= coercionByteSize(dstTy, dl)
424 [[maybe_unused]] mlir::Type coercedTy = ((slotTy == srcTy) ? dstTy : srcTy);
425 assert((offset == 0 ||
426 coercionByteSize(coercedTy, dl) < coercionByteSize(slotTy, dl)) &&
427 "a direct offset must land on the coerced side, the smaller one");
428 assert((offset == 0 ||
429 offset + dl.getTypeSize(coercedTy) <= dl.getTypeSize(slotTy)) &&
430 "coerce slot too small for offset access");
431 assert((offset == 0 || offset % dl.getTypeABIAlignment(coercedTy) == 0) &&
432 "a direct offset must be aligned for the coerced access");
434 mlir::OpBuilder::InsertionGuard guard(builder);
435 builder.setInsertionPointToStart(slotBlock);
436 auto alloca = cir::AllocaOp::create(builder, loc, getAllocaPtrTy(slotTy, dl),
437 builder.getStringAttr(
"coerce"),
438 builder.getI64IntegerAttr(allocaAlign));
439 createdOps.insert(alloca);
473mlir::Value emitCoercionToMemory(mlir::OpBuilder &builder, mlir::Location loc,
474 mlir::Type dstTy, mlir::Value src,
475 mlir::Block *slotBlock,
476 const mlir::DataLayout &dl,
477 SmallPtrSetImpl<mlir::Operation *> &createdOps,
479 mlir::Type srcTy = src.getType();
480 cir::AllocaOp alloca = createCoercionSlot(builder, loc, srcTy, dstTy,
481 slotBlock, dl, createdOps, offset);
482 mlir::Type slotTy = alloca.getAllocaType();
485 mlir::Value srcSlot = emitViewAtOffset(
486 builder, loc, srcTy, alloca, createdOps, srcTy == slotTy ? 0 : offset);
487 auto store = cir::StoreOp::create(builder, loc, src, srcSlot);
488 createdOps.insert(store);
491 return emitViewAtOffset(builder, loc, dstTy, alloca, createdOps,
492 dstTy == slotTy ? 0 : offset);
498emitAddrSpaceCoercion(mlir::OpBuilder &builder, mlir::Location loc,
499 mlir::Type dstTy, mlir::Value src,
500 SmallPtrSetImpl<mlir::Operation *> &createdOps,
502 auto srcPtrTy = dyn_cast<cir::PointerType>(src.getType());
503 auto dstPtrTy = dyn_cast<cir::PointerType>(dstTy);
504 if (offset || !srcPtrTy || !dstPtrTy ||
505 srcPtrTy.getPointee() != dstPtrTy.getPointee())
507 assert(srcPtrTy.getAddrSpace() != dstPtrTy.getAddrSpace() &&
508 "pointers to the same pointee must differ in address space");
509 auto cast = cir::CastOp::create(builder, loc, dstTy,
510 cir::CastKind::address_space, src);
511 createdOps.insert(cast);
519mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
520 mlir::Type dstTy, mlir::Value src,
521 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
522 SmallPtrSetImpl<mlir::Operation *> &createdOps,
524 if (mlir::Value cast =
525 emitAddrSpaceCoercion(builder, loc, dstTy, src, createdOps, offset))
528 mlir::Value dstSlot = emitCoercionToMemory(builder, loc, dstTy, src,
529 slotBlock, dl, createdOps, offset);
530 auto load = cir::LoadOp::create(builder, loc, dstSlot);
531 createdOps.insert(load);
537mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
538 mlir::Type dstTy, mlir::Value src,
539 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
541 SmallPtrSet<mlir::Operation *, 4> ignored;
542 return emitCoercion(builder, loc, dstTy, src, slotBlock, dl, ignored, offset);
554mlir::Block *coercionSlotBlock(mlir::Operation *op) {
555 if (
auto funcOp = op->getParentOfType<mlir::FunctionOpInterface>())
556 return &funcOp->getRegion(0).front();
557 mlir::Region *region = op->getParentRegion();
558 while (mlir::Region *outer = region->getParentRegion()) {
559 if (mlir::isa<mlir::ModuleOp>(outer->getParentOp()))
563 assert(!region->empty() &&
"coercion slot needs a block to hold the alloca");
564 return ®ion->front();
571static cir::LoadOp maybeGetSimpleLoad(mlir::Value val) {
572 cir::LoadOp load = val.getDefiningOp<cir::LoadOp>();
573 if (!load || load.getIsVolatile() || load.getMemOrder())
581static cir::LoadOp maybeGetCopyableRecordLoad(mlir::Value val) {
582 cir::LoadOp load = val.getDefiningOp<cir::LoadOp>();
583 if (!load || load.getMemOrder() ||
584 !mlir::isa<cir::RecordType>(load.getType()))
592static mlir::LogicalResult rejectAtomicRecordLoad(mlir::Operation *user,
594 auto load = val.getDefiningOp<cir::LoadOp>();
595 if (!load || !load.getMemOrder() ||
596 !mlir::isa<cir::RecordType>(load.getType()))
597 return mlir::success();
598 return user->emitOpError()
599 <<
"atomic load of a record operand not yet implemented in "
607static bool canReadInPlace(cir::LoadOp load, mlir::Type coercedTy,
621static mlir::Value emitInPlaceCoercedLoad(mlir::OpBuilder &builder,
622 mlir::Location loc, cir::LoadOp load,
623 mlir::Type coercedTy,
626 assert(src &&
"the record load must read an alloca");
627 mlir::OpBuilder::InsertionGuard guard(builder);
628 builder.setInsertionPoint(load);
629 SmallPtrSet<mlir::Operation *, 4> ignored;
630 mlir::Value view = emitViewAtOffset(builder, loc, coercedTy, load.getAddr(),
632 auto coerced = cir::LoadOp::create(builder, loc, view);
633 coerced.setAlignment(llvm::MinAlign(src.getAlignment(), offset));
643emitCopiedCoercionSlot(mlir::OpBuilder &builder, mlir::Location loc,
644 cir::LoadOp load, mlir::Type coercedTy,
645 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
647 auto recTy = cast<cir::RecordType>(load.getType());
650 srcAlign = src.getAlignment();
652 srcAlign = load.getAlignment().value_or(dl.getTypeABIAlignment(recTy));
653 SmallPtrSet<mlir::Operation *, 4> ignored;
654 cir::AllocaOp slot = createCoercionSlot(builder, loc, recTy, coercedTy,
655 slotBlock, dl, ignored, offset);
656 slot.setAlignment(std::max(slot.getAlignment(), srcAlign));
657 assert((offset == 0 || slot.getAllocaType() == recTy) &&
658 "a direct offset must leave the record as the slot's type");
659 mlir::OpBuilder::InsertionGuard guard(builder);
660 builder.setInsertionPoint(load);
661 mlir::Value recView =
662 emitViewAtOffset(builder, loc, recTy, slot, ignored, 0);
664 cir::CopyOp::create(builder, loc, recView, load.getAddr(),
665 builder.getI64IntegerAttr(slot.getAlignment()),
666 builder.getI64IntegerAttr(srcAlign));
667 copy.setIsVolatile(load.getIsVolatile());
674static mlir::Value emitCopiedCoercedLoad(mlir::OpBuilder &builder,
675 mlir::Location loc, cir::LoadOp load,
676 mlir::Type coercedTy,
677 mlir::Block *slotBlock,
678 const mlir::DataLayout &dl,
680 cir::AllocaOp slot = emitCopiedCoercionSlot(builder, loc, load, coercedTy,
681 slotBlock, dl, offset);
682 mlir::OpBuilder::InsertionGuard guard(builder);
683 builder.setInsertionPoint(load);
684 SmallPtrSet<mlir::Operation *, 4> ignored;
685 mlir::Value coercedView =
686 emitViewAtOffset(builder, loc, coercedTy, slot, ignored, offset);
687 auto coerced = cir::LoadOp::create(builder, loc, coercedView);
688 coerced.setAlignment(llvm::MinAlign(slot.getAlignment(), offset));
699static mlir::Value emitCoercedRecordLoad(
700 mlir::OpBuilder &builder, mlir::Location loc, cir::LoadOp load,
701 mlir::Type coercedTy, mlir::Block *slotBlock,
const mlir::DataLayout &dl,
703 if (!load.getIsVolatile() &&
704 canReadInPlace(load, coercedTy, cirDataLayout, offset))
705 return emitInPlaceCoercedLoad(builder, loc, load, coercedTy, offset);
706 return emitCopiedCoercedLoad(builder, loc, load, coercedTy, slotBlock, dl,
720static bool shouldSkipTailPadding(mlir::Value dst, mlir::Type ty) {
721 auto structTy = mlir::dyn_cast<cir::StructType>(ty);
726 for (
auto [member,
kind] : llvm::reverse(llvm::zip_equal(members, kinds)))
728 return kind == cir::RecordMemberKind::Pad;
750static mlir::LogicalResult
751rewireRecordUses(mlir::OpBuilder &builder, mlir::Location loc, mlir::Value val,
752 mlir::Value addr, uint64_t addrAlign,
753 const mlir::DataLayout &dl,
754 const SmallPtrSetImpl<mlir::Operation *> &exceptions) {
755 mlir::Type recTy = mlir::cast<cir::PointerType>(addr.getType()).getPointee();
756 assert(mlir::isa<cir::RecordType>(recTy) &&
"only a record is rewired");
759 for (mlir::OpOperand &use : val.getUses()) {
760 mlir::Operation *user = use.getOwner();
761 if (exceptions.contains(user))
763 if (
auto store = mlir::dyn_cast<cir::StoreOp>(user)) {
764 if (store.getMemOrder())
765 return store.emitOpError()
766 <<
"atomic store of a record argument or result not yet "
767 "implemented in CallConvLowering";
768 stores.push_back(store);
771 auto member = mlir::dyn_cast<cir::ExtractMemberOp>(user);
772 bool isFieldRead =
member && !mlir::isa<cir::RecordType>(
member.getType());
774 !mlir::isa<cir::CIRCallOpInterface, cir::ReturnOp>(user))
775 return user->emitOpError()
776 <<
"use of a record argument or result other than by a store, "
777 "call, return or member read not yet implemented in "
779 otherUses.push_back(&use);
782 mlir::IntegerAttr srcAlign = builder.getI64IntegerAttr(addrAlign);
783 for (cir::StoreOp store : stores) {
784 mlir::OpBuilder::InsertionGuard guard(builder);
785 builder.setInsertionPoint(store);
787 store.getAlignment().value_or(dl.getTypeABIAlignment(recTy));
789 cir::CopyOp::create(builder, store.getLoc(), store.getAddr(), addr,
790 builder.getI64IntegerAttr(dstAlign), srcAlign);
791 copy.setIsVolatile(store.getIsVolatile());
792 copy.setSkipTailPadding(shouldSkipTailPadding(store.getAddr(), recTy));
796 if (otherUses.empty())
797 return mlir::success();
798 auto load = cir::LoadOp::create(builder, loc, addr);
799 load.setAlignment(addrAlign);
800 for (mlir::OpOperand *use : otherUses)
802 return mlir::success();
809static mlir::LogicalResult
810replaceUsesFromSlot(mlir::OpBuilder &builder, mlir::Location loc,
811 mlir::Value val, mlir::Value view,
812 const mlir::DataLayout &dl,
813 const SmallPtrSetImpl<mlir::Operation *> &exceptions) {
814 if (!mlir::isa<cir::RecordType>(
815 mlir::cast<cir::PointerType>(view.getType()).getPointee())) {
816 auto load = cir::LoadOp::create(builder, loc, view);
817 val.replaceAllUsesExcept(load, exceptions);
818 return mlir::success();
821 assert(slot &&
"the view must start at the coercion slot");
822 return rewireRecordUses(builder, loc, val, view, slot.getAlignment(), dl,
833static mlir::LogicalResult
834emitCoercionAndReplaceUses(mlir::OpBuilder &builder, mlir::Location loc,
835 mlir::Type origTy, mlir::Value src, mlir::Value val,
836 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
837 SmallPtrSetImpl<mlir::Operation *> &createdOps,
839 if (mlir::Value cast = emitAddrSpaceCoercion(builder, loc, origTy, src,
840 createdOps, offset)) {
841 val.replaceAllUsesExcept(cast, createdOps);
842 return mlir::success();
844 mlir::Value view = emitCoercionToMemory(builder, loc, origTy, src, slotBlock,
845 dl, createdOps, offset);
846 return replaceUsesFromSlot(builder, loc, val, view, dl, createdOps);
859insertReturnCoercion(mlir::FunctionOpInterface funcOp, mlir::Type origRetTy,
860 mlir::Type coercedRetTy, mlir::OpBuilder &builder,
861 const mlir::DataLayout &dl,
864 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
865 for (cir::ReturnOp r : returns) {
866 if (r.getInput().empty())
868 mlir::Value origVal = r.getInput()[0];
869 if (origVal.getType() == coercedRetTy)
872 if (failed(rejectAtomicRecordLoad(r, origVal)))
873 return mlir::failure();
874 if (cir::LoadOp recLoad = maybeGetCopyableRecordLoad(origVal)) {
875 mlir::Value coerced = emitCoercedRecordLoad(
876 builder, r.getLoc(), recLoad, coercedRetTy,
877 &funcOp->getRegion(0).front(), dl, cirDataLayout, offset);
878 r->setOperand(0, coerced);
879 if (recLoad->use_empty())
884 builder.setInsertionPoint(r);
885 mlir::Value coerced =
886 emitCoercion(builder, r.getLoc(), coercedRetTy, origVal,
887 &funcOp->getRegion(0).front(), dl, offset);
888 r->setOperand(0, coerced);
890 return mlir::success();
895static cir::LoadOp getWholeRecordLoad(mlir::Value recordVal) {
896 cir::LoadOp load = maybeGetSimpleLoad(recordVal);
922static cir::LoadOp forwardableIndirectLoad(cir::LoadOp load,
924 if (!load || load.getAddr().getType() !=
925 cir::PointerType::get(load.getResult().getType()))
928 return slot && slot.getAlignment() >= minAlign ? load : cir::LoadOp();
941static bool storageUnwrittenBetween(cir::LoadOp load, mlir::Operation *call,
942 mlir::AliasAnalysis &aliasAnalysis) {
943 if (load->getBlock() != call->getBlock())
945 for (mlir::Operation *op = load->getNextNode(); op != call;
946 op = op->getNextNode()) {
947 assert(op &&
"the load must precede the call in the block they share");
948 if (op->getNumRegions() != 0 ||
949 aliasAnalysis.getModRef(op, load.getAddr()).isMod())
959static mlir::IntegerAttr loadSourceAlignment(cir::LoadOp load,
960 mlir::OpBuilder &builder) {
961 if (mlir::IntegerAttr stated = load.getAlignmentAttr())
964 return builder.getI64IntegerAttr(slot.getAlignment());
965 return builder.getI64IntegerAttr(1);
979static void emitStructFieldArgs(mlir::OpBuilder &builder, mlir::Location loc,
981 SmallVectorImpl<mlir::Value> &newArgs,
982 SmallVectorImpl<cir::LoadOp> &deadRecordLoads) {
983 cir::LoadOp srcLoad = getWholeRecordLoad(structVal);
986 mlir::OpBuilder::InsertionGuard guard(builder);
987 builder.setInsertionPoint(srcLoad);
988 cir::PointerType baseTy = srcLoad.getAddr().getType();
989 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
990 mlir::Type fieldPtrTy =
991 cir::PointerType::get(fieldTy, baseTy.getAddrSpace());
992 mlir::Value fieldPtr = cir::GetMemberOp::create(
993 builder, loc, fieldPtrTy, srcLoad.getAddr(),
"",
995 newArgs.push_back(cir::LoadOp::create(builder, loc, fieldPtr));
997 deadRecordLoads.push_back(srcLoad);
1001 cir::ExtractMemberOp::create(builder, loc, structVal, f));
1012 for (mlir::Operation *load : uniqueLoads)
1013 if (load->use_empty())
1019static cir::StoreOp maybeFindParamSpill(mlir::BlockArgument blockArg) {
1020 if (!blockArg.hasOneUse())
1022 auto store = dyn_cast<cir::StoreOp>(*blockArg.user_begin());
1023 if (!store || store.getValue() != blockArg)
1034static std::pair<cir::StoreOp, cir::AllocaOp>
1035findParamSpill(mlir::BlockArgument blockArg) {
1036 if (blockArg.use_empty())
1038 cir::StoreOp store = maybeFindParamSpill(blockArg);
1039 assert(store &&
"non-byval arg's only use must be its CIRGen param spill");
1040 return {store, cast<cir::AllocaOp>(store.getAddr().getDefiningOp())};
1064insertArgCoercion(mlir::FunctionOpInterface funcOp,
1065 const FunctionClassification &fc, mlir::OpBuilder &builder,
1066 const mlir::DataLayout &dl,
bool hasSRetArg,
1067 SmallVectorImpl<std::pair<cir::AllocaOp, mlir::BlockArgument>>
1068 &pendingParamSlots) {
1069 mlir::Region &body = funcOp->getRegion(0);
1071 return mlir::success();
1072 mlir::Block &entry = body.front();
1078 unsigned blockArgIdx = hasSRetArg ? 1 : 0;
1080 for (
const ArgClassification &ac : fc.argInfos) {
1081 assert(blockArgIdx < entry.getNumArguments() &&
1082 "classification count must not exceed entry block arguments");
1084 if (ac.kind == ArgKind::Expand) {
1088 mlir::BlockArgument origArg = entry.getArgument(blockArgIdx);
1089 auto recTy = cast<cir::RecordType>(origArg.getType());
1091 "Expand classification requires a struct type, not a union");
1093 assert(numFields > 0 &&
1094 "Expand classification requires at least one struct field");
1095 mlir::Location loc = funcOp.getLoc();
1105 cir::StoreOp paramStore;
1106 cir::AllocaOp destAlloca;
1107 if (!origArg.use_empty()) {
1108 assert(origArg.hasOneUse() &&
1109 "Expand arg must have exactly one use (the CIRGen param spill)");
1110 paramStore = cast<cir::StoreOp>(*origArg.user_begin());
1111 assert(paramStore.getValue() == origArg &&
1112 "Expand arg's use must be the value operand of its store");
1113 destAlloca = cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
1120 mlir::Operation *fieldStoreInsertPt =
nullptr;
1122 fieldStoreInsertPt = paramStore->getNextNode();
1123 assert(fieldStoreInsertPt &&
1124 "param spill must be followed by a block terminator");
1125 paramStore->erase();
1136 builder.setInsertionPoint(fieldStoreInsertPt);
1137 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
1139 origArg.setType(fieldTy);
1141 entry.insertArgument(blockArgIdx + f, fieldTy, loc);
1144 mlir::Type fieldPtrTy = cir::PointerType::get(
1146 mlir::cast<cir::PointerType>(destAlloca.getType()).getAddrSpace());
1147 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
1150 cir::StoreOp::create(builder, loc, entry.getArgument(blockArgIdx + f),
1154 blockArgIdx += numFields;
1158 mlir::BlockArgument blockArg = entry.getArgument(blockArgIdx);
1166 unsigned numFields = flatTy.getNumElements();
1167 assert(numFields >= 2 &&
"getFlattenedCoercedType guarantees >1 fields");
1168 Type origTy = blockArg.getType();
1169 Location loc = funcOp.getLoc();
1172 blockArg.setType(flatTy.getElementType(0));
1173 for (
unsigned f = 1; f < numFields; ++f)
1174 entry.insertArgument(blockArgIdx + f, flatTy.getElementType(f), loc);
1177 builder.setInsertionPointToStart(&entry);
1178 assert(!ac.directOffset &&
1179 "the coerced struct starts at slot offset 0 here, so a flattened "
1180 "coercion cannot honor a direct offset");
1181 SmallPtrSet<Operation *, 8> flattenOps;
1183 if (origTy == flatTy) {
1184 slot = cir::AllocaOp::create(
1185 builder, loc, getAllocaPtrTy(flatTy, dl),
1186 builder.getStringAttr(
"coerce"),
1187 builder.getI64IntegerAttr(dl.getTypeABIAlignment(flatTy)));
1188 flattenOps.insert(slot);
1190 slot = createCoercionSlot(builder, loc, flatTy, origTy, &entry, dl,
1193 Value flatView = emitViewAtOffset(builder, loc, flatTy, slot, flattenOps,
1195 auto flatPtrTy = cast<cir::PointerType>(flatView.getType());
1196 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
1198 cir::PointerType::get(fieldTy, flatPtrTy.getAddrSpace());
1199 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
1202 flattenOps.insert(fieldPtr);
1203 auto storeOp = cir::StoreOp::create(
1204 builder, loc, entry.getArgument(blockArgIdx + f), fieldPtr);
1205 flattenOps.insert(storeOp);
1210 Value origView = emitViewAtOffset(builder, loc, origTy, slot, flattenOps,
1212 if (failed(replaceUsesFromSlot(builder, loc, blockArg, origView, dl,
1214 return mlir::failure();
1216 blockArgIdx += numFields;
1220 if (ac.kind == ArgKind::Direct && ac.coercedType) {
1221 mlir::Type oldArgTy = blockArg.getType();
1222 mlir::Type newArgTy = ac.coercedType;
1223 if (oldArgTy == newArgTy) {
1227 blockArg.setType(newArgTy);
1231 builder.setInsertionPointToStart(&entry);
1232 SmallPtrSet<mlir::Operation *, 4> coercionOps;
1233 if (failed(emitCoercionAndReplaceUses(builder, funcOp.getLoc(), oldArgTy,
1234 blockArg, blockArg, &entry, dl,
1235 coercionOps, ac.directOffset)))
1236 return mlir::failure();
1237 }
else if (ac.kind == ArgKind::Indirect) {
1244 auto ptrTy = cir::PointerType::get(blockArg.getType());
1252 auto [paramStore, destAlloca] = findParamSpill(blockArg);
1255 paramStore->erase();
1258 blockArg.setType(ptrTy);
1267 pendingParamSlots.emplace_back(destAlloca, blockArg);
1274 cir::StoreOp paramStore;
1275 if (mlir::isa<cir::RecordType>(blockArg.getType()))
1276 paramStore = maybeFindParamSpill(blockArg);
1280 mlir::Value destAddr;
1281 mlir::IntegerAttr destAlign;
1282 mlir::Operation *spillPos =
nullptr;
1283 bool spillIsVolatile =
false;
1285 if (paramStore.getMemOrder())
1286 return paramStore.emitOpError()
1287 <<
"atomic store of a record argument or result not yet "
1288 "implemented in CallConvLowering";
1289 spillIsVolatile = paramStore.getIsVolatile();
1290 destAddr = paramStore.getAddr();
1292 destAlign = paramStore.getAlignmentAttr();
1293 if (!destAlign && destAlloca)
1294 destAlign = destAlloca.getAlignmentAttr();
1295 spillPos = paramStore->getNextNode();
1296 assert(spillPos &&
"param spill must be followed by a block "
1298 paramStore->erase();
1301 blockArg.setType(ptrTy);
1304 mlir::OpBuilder::InsertionGuard guard(builder);
1305 builder.setInsertionPoint(spillPos);
1306 auto copy = cir::CopyOp::create(
1307 builder, funcOp.getLoc(), destAddr, blockArg, destAlign,
1308 builder.getI64IntegerAttr(ac.indirectAlign.value()));
1309 copy.setIsVolatile(spillIsVolatile);
1310 copy.setSkipTailPadding(
1311 shouldSkipTailPadding(destAddr, ptrTy.getPointee()));
1312 }
else if (!blockArg.use_empty()) {
1313 builder.setInsertionPointToStart(&entry);
1314 if (mlir::isa<cir::RecordType>(ptrTy.getPointee())) {
1315 SmallPtrSet<mlir::Operation *, 1> noExceptions;
1316 if (failed(rewireRecordUses(builder, funcOp.getLoc(), blockArg,
1317 blockArg, ac.indirectAlign.value(), dl,
1319 return mlir::failure();
1322 cir::LoadOp::create(builder, funcOp.getLoc(), blockArg);
1323 SmallPtrSet<mlir::Operation *, 1> loadOps = {loadOp};
1324 blockArg.replaceAllUsesExcept(loadOp.getResult(), loadOps);
1333 return mlir::success();
1364void insertSRetStores(mlir::FunctionOpInterface funcOp, mlir::Type origRetTy,
1365 mlir::OpBuilder &builder) {
1366 mlir::Value sretPtr = funcOp.getArguments()[0];
1369 funcOp->walk([&](cir::ReturnOp retOp) { returnOps.push_back(retOp); });
1371 cir::AllocaOp retAlloca =
nullptr;
1372 for (cir::ReturnOp retOp : returnOps) {
1375 assert(!retOp.getInput().empty() &&
1376 "cir.return in sret function must have an operand");
1378 cir::LoadOp retLoad =
1379 mlir::cast<cir::LoadOp>(retOp.getInput()[0].getDefiningOp());
1387 retAlloca = mlir::cast<cir::AllocaOp>(retLoad.getAddr().getDefiningOp());
1388 retAlloca.getResult().replaceAllUsesWith(sretPtr);
1394 builder.setInsertionPoint(retOp);
1395 cir::ReturnOp::create(builder, retOp.getLoc());
1397 if (retLoad.use_empty())
1416 builder.getNamedAttr(
"llvm.sret", mlir::TypeAttr::get(retTy)));
1418 builder.getNamedAttr(
"llvm.align", builder.getI64IntegerAttr(align)));
1421 builder.getNamedAttr(
"llvm.noalias", builder.getUnitAttr()));
1422 attrs.push_back(builder.getNamedAttr(
"llvm.writable", builder.getUnitAttr()));
1424 builder.getNamedAttr(
"llvm.dead_on_unwind", builder.getUnitAttr()));
1434void applySretSlotAttrs(cir::CallOp newCall, mlir::ArrayAttr argAttrs,
1435 mlir::Type retTy, uint64_t align,
1436 mlir::OpBuilder &builder) {
1437 mlir::MLIRContext *ctx = newCall->getContext();
1439 buildSretSlotAttrs(builder, retTy, align,
false);
1442 newArgAttrs.reserve(newCall.getArgOperands().size());
1443 newArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1445 llvm::append_range(newArgAttrs, argAttrs);
1446 assert(newArgAttrs.size() <= newCall.getArgOperands().size() &&
1447 "arg_attrs wider than the rewritten call's operand list");
1448 newArgAttrs.resize(newCall.getArgOperands().size(),
1449 mlir::DictionaryAttr::get(ctx));
1450 newCall->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, newArgAttrs));
1457static void copyCallAttributes(cir::CallOp source, cir::CallOp target) {
1458 source->getName().walkInherentAttrs(source, [&](llvm::StringRef name,
1459 mlir::Attribute &attr) {
1460 if (name != cir::CIRDialect::getCalleeAttrName())
1461 target->setInherentAttr(mlir::StringAttr::get(source->getContext(), name),
1464 for (mlir::NamedAttribute attr : source->getDiscardableAttrs())
1465 if (!target->hasDiscardableAttr(
attr.getName()))
1466 target->setDiscardableAttr(
attr.getName(),
attr.getValue());
1477static void prependIndirectCallee(cir::CallOp call,
1478 SmallVectorImpl<mlir::Value> &args,
1479 mlir::Type retTy,
unsigned numEllipsisArgs,
1480 mlir::OpBuilder &builder) {
1481 if (!call.isIndirect())
1483 assert(numEllipsisArgs <= args.size() &&
1484 "more ellipsis arguments than arguments");
1485 mlir::Value calleePtr = call.getIndirectCall();
1487 paramTypes.reserve(args.size() - numEllipsisArgs);
1488 llvm::transform(
ArrayRef(args).drop_back(numEllipsisArgs),
1489 std::back_inserter(paramTypes),
1490 [](mlir::Value v) {
return v.getType(); });
1500 assert((calleeFnTy.isVarArg() || numEllipsisArgs == 0) &&
1501 "only a variadic callee takes arguments through an ellipsis");
1502 auto newPtrTy = cir::PointerType::get(
1503 cir::FuncType::get(paramTypes, retTy, calleeFnTy.isVarArg()));
1504 if (calleePtr.getType() != newPtrTy)
1505 calleePtr = cir::CastOp::create(builder, call.getLoc(), newPtrTy,
1506 cir::CastKind::bitcast, calleePtr);
1507 args.insert(args.begin(), calleePtr);
1520mlir::LogicalResult rewriteIndirectReturnCall(
1521 cir::CallOp call,
const FunctionClassification &fc,
1524 mlir::OpBuilder &builder,
const mlir::DataLayout &dl) {
1525 mlir::MLIRContext *ctx = call->getContext();
1526 auto ptrTy = cir::PointerType::get(origRetTy);
1527 builder.setInsertionPoint(call);
1528 uint64_t sretAlign = fc.returnInfo.indirectAlign.value();
1541 mlir::Value sretSlot =
nullptr;
1542 cir::StoreOp reuseStore =
nullptr;
1543 if (call.getResult().hasOneUse()) {
1544 mlir::Operation *user = *call.getResult().getUsers().begin();
1545 if (
auto store = mlir::dyn_cast<cir::StoreOp>(user))
1546 if (store.getValue() == call.getResult() &&
1547 store.getAddr().getType() == ptrTy &&
1548 mlir::DominanceInfo().properlyDominates(store.getAddr(), call)) {
1549 sretSlot = store.getAddr();
1554 auto alloca = cir::AllocaOp::create(
1555 builder, call.getLoc(), ptrTy,
1556 builder.getStringAttr(
"sret"),
1557 builder.getI64IntegerAttr(sretAlign));
1562 sretArgs.push_back(sretSlot);
1563 sretArgs.append(newArgs.begin(), newArgs.end());
1567 mlir::Type sretVoidTy = cir::VoidType::get(ctx);
1568 prependIndirectCallee(call, sretArgs, sretVoidTy, numEllipsisArgs, builder);
1569 auto newCall = cir::CallOp::create(
1570 builder, call.getLoc(), call.getCalleeAttr(), sretVoidTy, sretArgs);
1571 copyCallAttributes(call, newCall);
1572 newCall->removeAttr(
"res_attrs");
1577 mlir::ArrayAttr argAttrs = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1578 if (needsArgAttrUpdate(fc))
1579 argAttrs = updateArgAttrs(ctx, call->getParentOfType<mlir::ModuleOp>(),
1580 origCallArgTypes, argAttrs, fc, dl);
1581 applySretSlotAttrs(newCall, argAttrs, origRetTy, sretAlign, builder);
1587 reuseStore->erase();
1588 }
else if (mlir::isa<cir::RecordType>(origRetTy)) {
1589 builder.setInsertionPointAfter(newCall);
1590 SmallPtrSet<mlir::Operation *, 1> noExceptions;
1591 if (failed(rewireRecordUses(builder, call.getLoc(), call.getResult(),
1592 sretSlot, sretAlign, dl, noExceptions)))
1593 return mlir::failure();
1595 builder.setInsertionPointAfter(newCall);
1596 auto load = cir::LoadOp::create(builder, call.getLoc(), origRetTy, sretSlot,
1600 mlir::IntegerAttr(),
1601 cir::SyncScopeKindAttr(),
1602 cir::MemOrderAttr(),
1604 call.getResult().replaceAllUsesWith(load);
1607 return mlir::success();
1612bool isSSERegisterClass(mlir::Type ty) {
1613 return mlir::isa<cir::VectorType, cir::FPTypeInterface>(ty);
1619 assert(call.isIndirect() &&
"expected an indirect call");
1620 return cast<cir::FuncType>(
1621 cast<cir::PointerType>(call.getIndirectCall().getType()).getPointee());
1630 cir::FuncOp funcOp,
unsigned argNo,
const ArgClassification &ac,
1631 mlir::BlockArgument blockArg, mlir::DominanceInfo &dom,
1632 SmallPtrSetImpl<mlir::Operation *> &claimedSlots) {
1638 cir::StoreOp extraSpill;
1639 mlir::Operation *otherUse =
nullptr;
1641 for (mlir::OpOperand &use : blockArg.getUses()) {
1642 auto store = dyn_cast<cir::StoreOp>(use.getOwner());
1643 if (store && store.getValue() == blockArg) {
1657 auto call = dyn_cast<cir::CIRCallOpInterface>(use.getOwner());
1658 if (call && llvm::is_contained(call.getArgOperands(), blockArg))
1659 callArgs.push_back(&use);
1661 otherUse = use.getOwner();
1665 return extraSpill->emitOpError()
1666 <<
"non-byval parameter " << argNo
1667 <<
" spilled more than once is not yet implemented in "
1671 return otherUse->emitOpError()
1672 <<
"non-byval parameter " << argNo
1673 <<
" consumed other than as a call argument is not yet implemented "
1674 "in CallConvLowering";
1679 if (callArgs.empty())
1680 return mlir::success();
1687 mlir::OpBuilder builder(funcOp.getContext());
1688 builder.setInsertionPointToStart(blockArg.getOwner());
1689 auto synthesized = cir::AllocaOp::create(
1690 builder, funcOp.getLoc(), cir::PointerType::get(blockArg.getType()),
1691 builder.getStringAttr(
"nonbyval.param"),
1692 builder.getI64IntegerAttr(ac.indirectAlign.value()));
1694 cir::StoreOp::create(builder, funcOp.getLoc(), blockArg, synthesized);
1702 cir::AllocaOp slot = spill.getAddr().getDefiningOp<cir::AllocaOp>();
1704 spill.getAddr().getType() != cir::PointerType::get(blockArg.getType()))
1705 return spill->emitOpError()
1706 <<
"non-byval parameter " << argNo
1707 <<
" spilled to storage that cannot take the incoming pointer is "
1708 "not yet implemented in CallConvLowering";
1714 if (!claimedSlots.insert(slot).second)
1715 return spill->emitOpError()
1716 <<
"non-byval parameter " << argNo
1717 <<
" sharing its spill slot with another parameter is not yet "
1718 "implemented in CallConvLowering";
1723 slot.setAlignment(ac.indirectAlign.value());
1725 if (callArgs.empty())
1726 return mlir::success();
1730 for (mlir::OpOperand *callArg : callArgs)
1731 if (!dom.properlyDominates(spill, callArg->getOwner()))
1732 return callArg->getOwner()->emitOpError()
1733 <<
"non-byval parameter " << argNo
1734 <<
" read before its spill is not yet implemented in "
1747 mlir::OpBuilder builder(spill);
1748 builder.setInsertionPointAfter(spill);
1749 auto reload = cir::LoadOp::create(builder, spill.getLoc(), slot);
1750 reload.setAlignment(ac.indirectAlign.value());
1751 for (mlir::OpOperand *callArg : callArgs)
1752 callArg->set(reload.getResult());
1753 return mlir::success();
1757 cir::FuncOp funcOp,
const FunctionClassification &fc) {
1758 if (!funcOp.isDefinition())
1759 return mlir::success();
1760 mlir::Region &body = funcOp->getRegion(0);
1762 return mlir::success();
1763 mlir::Block &entry = body.front();
1764 mlir::DominanceInfo dom;
1765 SmallPtrSet<mlir::Operation *, 4> claimedSlots;
1771 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1772 if (ac.kind != ArgKind::Indirect || ac.byVal)
1774 assert(idx < entry.getNumArguments() &&
1775 "classification count must not exceed entry block arguments");
1777 funcOp, idx, ac, entry.getArgument(idx), dom, claimedSlots)))
1778 return mlir::failure();
1780 return mlir::success();
1784 for (
auto [slot, incoming] : pendingParamSlots) {
1785 slot.getResult().replaceAllUsesWith(incoming);
1788 pendingParamSlots.clear();
1792 mlir::FunctionOpInterface funcOpInterface,
const FunctionClassification &fc,
1793 mlir::OpBuilder &builder) {
1799 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(funcOpInterface);
1801 if (!fc.needsRewrite())
1802 return mlir::success();
1806 mlir::MLIRContext *ctx = funcOp->getContext();
1811 assert(oldResultTypes.size() <= 1 &&
1812 "CIR functions return zero or one value");
1815 if (mlir::failed(buildNewArgTypes(oldArgTypes, fc, newArgTypes,
1816 [&]() {
return funcOp.emitOpError(); })))
1817 return mlir::failure();
1819 mlir::Type voidTy = cir::VoidType::get(ctx);
1820 mlir::Type origRetTy = oldResultTypes.empty() ? voidTy : oldResultTypes[0];
1821 mlir::Type newRetTy = computeNewReturnType(
1822 origRetTy, fc.returnInfo, ctx, [&]() { return funcOp.emitOpError(); });
1824 return mlir::failure();
1834 fc.returnInfo.kind == ArgKind::Indirect && !oldResultTypes.empty();
1836 newArgTypes.insert(newArgTypes.begin(), cir::PointerType::get(origRetTy));
1838 if (funcOp.isDefinition()) {
1839 mlir::Region &body = funcOp->getRegion(0);
1840 if (!body.empty()) {
1845 body.front().insertArgument(0u, cir::PointerType::get(origRetTy),
1847 insertSRetStores(funcOp, origRetTy, builder);
1856 if (failed(insertArgCoercion(funcOp, fc, builder, dl, hasSRet,
1857 pendingParamSlots)))
1858 return mlir::failure();
1863 if (fc.returnInfo.kind == ArgKind::Direct && fc.returnInfo.coercedType &&
1864 !oldResultTypes.empty() && fc.returnInfo.coercedType != origRetTy &&
1865 failed(insertReturnCoercion(
1866 funcOp, origRetTy, fc.returnInfo.coercedType, builder, dl,
1867 cirDataLayout, fc.returnInfo.directOffset)))
1868 return mlir::failure();
1870 mlir::Block &entry = body.front();
1879 unsigned blockArgIdx = hasSRet ? 1 : 0;
1880 for (
auto [i, ac] : llvm::enumerate(fc.argInfos)) {
1881 if (blockArgIdx >= entry.getNumArguments())
1883 if (ac.kind == ArgKind::Ignore) {
1884 mlir::BlockArgument arg = entry.getArgument(blockArgIdx);
1885 if (!arg.use_empty()) {
1886 builder.setInsertionPointToStart(&entry);
1887 mlir::Value poison =
1888 createIgnoredValue(builder, funcOp.getLoc(), arg.getType());
1889 arg.replaceAllUsesWith(poison);
1891 entry.eraseArgument(blockArgIdx);
1895 blockArgIdx += flatTy.getNumElements();
1896 else if (ac.kind == ArgKind::Expand)
1897 blockArgIdx += cast<cir::RecordType>(oldArgTypes[i]).getNumElements();
1909 if (fc.returnInfo.kind == ArgKind::Ignore && !oldResultTypes.empty()) {
1910 assert(mlir::isa<cir::VoidType>(newRetTy) &&
1911 "Ignore-return path requires the new return type to be void");
1913 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
1914 for (cir::ReturnOp r : returns) {
1915 if (r.getNumOperands() == 0)
1917 builder.setInsertionPoint(r);
1918 cir::ReturnOp::create(builder, r.getLoc());
1924 mlir::Type newFnTy = funcOp.cloneTypeWith(newArgTypes, newResultTypes);
1925 funcOp.setFunctionTypeAttr(mlir::TypeAttr::get(newFnTy));
1929 if (hasSRet || needsArgAttrUpdate(fc)) {
1930 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1931 mlir::ArrayAttr updated =
1932 updateArgAttrs(ctx, module, oldArgTypes, existing, fc, dl);
1938 builder, origRetTy, fc.returnInfo.indirectAlign.value(),
1939 funcOp.isDefinition());
1941 withSret.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1942 llvm::append_range(withSret, updated);
1943 funcOp->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, withSret));
1945 funcOp->setAttr(
"arg_attrs", updated);
1949 if (mlir::isa<cir::VoidType>(newRetTy)) {
1950 funcOp->removeAttr(
"res_attrs");
1951 }
else if (fc.returnInfo.kind == ArgKind::Extend ||
1952 fc.returnInfo.kind == ArgKind::Direct) {
1953 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1954 if (mlir::ArrayAttr updated =
1955 updateResAttrs(ctx, existing, fc.returnInfo, origRetTy, dl))
1956 funcOp->setAttr(
"res_attrs", updated);
1959 return mlir::success();
1964 const FunctionClassification &fc,
1965 mlir::OpBuilder &builder) {
1977 unsigned numOperands =
1978 mlir::cast<cir::CIRCallOpInterface>(callOp).getNumArgOperands();
1979 if (numOperands > fc.argInfos.size())
1980 return callOp->emitOpError()
1981 <<
"variadic arguments not yet implemented in CallConvLowering";
1982 if (numOperands < fc.argInfos.size())
1983 return callOp->emitOpError()
1984 <<
"call passes fewer arguments than the callee declares, which is "
1985 "not yet implemented in CallConvLowering";
1987 if (!fc.needsRewrite())
1988 return mlir::success();
1990 if (mlir::isa<cir::TryCallOp>(callOp))
1991 return callOp->emitOpError()
1992 <<
"TryCallOp not yet implemented in CallConvLowering";
1994 auto call = mlir::cast<cir::CallOp>(callOp);
1995 mlir::MLIRContext *ctx = callOp->getContext();
1996 mlir::Block *slotBlock = coercionSlotBlock(call);
1998 builder.setInsertionPoint(call);
2001 mlir::ValueRange argOperands = call.getArgOperands();
2002 newArgs.reserve(argOperands.size());
2014 llvm::append_range(origCallArgTypes, argOperands.getTypes());
2020 unsigned numDeclared = fc.argInfos.size();
2021 if (call.isIndirect()) {
2023 assert(numDeclared <= fc.argInfos.size() &&
2024 "the classification covers at least the declared parameters");
2028 std::optional<unsigned> ellipsisStart;
2029 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
2030 if (idx == numDeclared)
2031 ellipsisStart = newArgs.size();
2032 if (ac.kind == ArgKind::Ignore)
2034 mlir::Value arg = argOperands[idx];
2042 if (arg.getType() != flatTy) {
2043 SmallPtrSet<mlir::Operation *, 4> coercionOps;
2044 assert(!ac.directOffset &&
2045 "the coerced struct starts at slot offset 0 here, so a "
2046 "flattened coercion cannot honor a direct offset");
2047 if (failed(rejectAtomicRecordLoad(call, arg)))
2048 return mlir::failure();
2049 mlir::OpBuilder::InsertionGuard guard(builder);
2050 mlir::Value coercedPtr;
2051 uint64_t baseAlign = 0;
2052 if (cir::LoadOp recLoad = maybeGetCopyableRecordLoad(arg)) {
2053 mlir::Value base = recLoad.getAddr();
2054 if (!recLoad.getIsVolatile() &&
2055 canReadInPlace(recLoad, flatTy, cirDataLayout, 0)) {
2057 assert(baseAlloca &&
"canReadInPlace requires an alloca");
2058 baseAlign = baseAlloca.getAlignment();
2060 base = emitCopiedCoercionSlot(builder, call.getLoc(), recLoad,
2061 flatTy, slotBlock, dl, 0);
2063 builder.setInsertionPoint(recLoad);
2064 coercedPtr = emitViewAtOffset(builder, call.getLoc(), flatTy, base,
2066 deadRecordLoads.push_back(recLoad);
2069 emitCoercionToMemory(builder, call.getLoc(), flatTy, arg,
2070 slotBlock, dl, coercionOps, 0);
2072 auto coercedPtrTy = mlir::cast<cir::PointerType>(coercedPtr.getType());
2073 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
2074 mlir::Type fieldPtrTy =
2075 cir::PointerType::get(fieldTy, coercedPtrTy.getAddrSpace());
2077 cir::GetMemberOp::create(builder, call.getLoc(), fieldPtrTy,
2079 auto field = cir::LoadOp::create(builder, call.getLoc(), fieldTy,
2080 fieldPtr.getResult());
2085 llvm::MinAlign(baseAlign, flatTy.getElementOffset(dl, f)));
2086 newArgs.push_back(field);
2089 emitStructFieldArgs(builder, call.getLoc(), arg, flatTy, newArgs,
2092 }
else if (ac.kind == ArgKind::Expand) {
2095 auto recTy = cast<cir::RecordType>(arg.getType());
2097 "Expand classification requires a struct type, not a union");
2098 emitStructFieldArgs(builder, call.getLoc(), arg, recTy, newArgs,
2100 }
else if (ac.kind == ArgKind::Direct && ac.coercedType &&
2101 arg.getType() != ac.coercedType) {
2102 if (failed(rejectAtomicRecordLoad(call, arg)))
2103 return mlir::failure();
2104 if (cir::LoadOp recLoad = maybeGetCopyableRecordLoad(arg)) {
2105 newArgs.push_back(emitCoercedRecordLoad(
2106 builder, call.getLoc(), recLoad, ac.coercedType, slotBlock, dl,
2107 cirDataLayout, ac.directOffset));
2108 deadRecordLoads.push_back(recLoad);
2110 newArgs.push_back(emitCoercion(builder, call.getLoc(), ac.coercedType,
2111 arg, slotBlock, dl, ac.directOffset));
2113 }
else if (ac.kind == ArgKind::Indirect) {
2114 uint64_t minAlign = ac.indirectAlign.value();
2122 cir::LoadOp srcLoad;
2123 if (!ac.byVal || mlir::isa<cir::RecordType>(arg.getType()))
2124 srcLoad = maybeGetSimpleLoad(arg);
2132 cir::LoadOp fwdLoad = forwardableIndirectLoad(srcLoad, minAlign);
2134 return call->emitOpError()
2135 <<
"non-byval indirect argument that does not name the "
2136 "caller's storage is not yet implemented in "
2138 newArgs.push_back(fwdLoad.getAddr());
2139 deadRecordLoads.push_back(fwdLoad);
2149 if (cir::LoadOp fwdLoad = forwardableIndirectLoad(srcLoad, minAlign);
2150 fwdLoad && storageUnwrittenBetween(fwdLoad, call.getOperation(),
2152 newArgs.push_back(fwdLoad.getAddr());
2153 deadRecordLoads.push_back(fwdLoad);
2159 if (failed(rejectAtomicRecordLoad(call, arg)))
2160 return mlir::failure();
2162 srcLoad = maybeGetCopyableRecordLoad(arg);
2163 auto ptrTy = cir::PointerType::get(arg.getType());
2164 mlir::IntegerAttr slotAlign = builder.getI64IntegerAttr(minAlign);
2165 mlir::OpBuilder::InsertionGuard guard(builder);
2167 builder.setInsertionPoint(srcLoad);
2170 cir::AllocaOp::create(builder, call.getLoc(), ptrTy,
2171 builder.getStringAttr(
"byval"), slotAlign);
2173 auto copy = cir::CopyOp::create(builder, call.getLoc(), slot,
2174 srcLoad.getAddr(), slotAlign,
2175 loadSourceAlignment(srcLoad, builder));
2176 copy.setIsVolatile(srcLoad.getIsVolatile());
2177 deadRecordLoads.push_back(srcLoad);
2182 cir::StoreOp::create(builder, call.getLoc(), arg, slot);
2184 newArgs.push_back(slot);
2186 newArgs.push_back(arg);
2189 unsigned numEllipsisArgs =
2190 ellipsisStart ? newArgs.size() - *ellipsisStart : 0;
2192 bool hasResult = call.getNumResults() > 0;
2193 mlir::Type origRetTy =
2194 hasResult ? call.getResult().getType() : cir::VoidType::get(ctx);
2200 if (fc.returnInfo.kind == ArgKind::Indirect && hasResult) {
2201 if (failed(rewriteIndirectReturnCall(call, fc, newArgs, numEllipsisArgs,
2202 origRetTy, origCallArgTypes, builder,
2204 return mlir::failure();
2205 eraseDeadRecordLoads(deadRecordLoads);
2206 return mlir::success();
2209 mlir::Type callRetTy = origRetTy;
2210 if (fc.returnInfo.kind == ArgKind::Ignore && hasResult)
2211 callRetTy = cir::VoidType::get(ctx);
2212 bool returnNeedsCoercion =
2213 hasResult && fc.returnInfo.kind == ArgKind::Direct &&
2214 fc.returnInfo.coercedType && fc.returnInfo.coercedType != origRetTy;
2215 if (returnNeedsCoercion)
2216 callRetTy = fc.returnInfo.coercedType;
2218 builder.setInsertionPoint(call);
2219 prependIndirectCallee(call, newArgs, callRetTy, numEllipsisArgs, builder);
2220 auto newCall = cir::CallOp::create(builder, call.getLoc(),
2221 call.getCalleeAttr(), callRetTy, newArgs);
2222 copyCallAttributes(call, newCall);
2227 if (returnNeedsCoercion) {
2228 builder.setInsertionPointAfter(newCall);
2229 SmallPtrSet<mlir::Operation *, 4> coercionOps;
2230 if (failed(emitCoercionAndReplaceUses(builder, call.getLoc(), origRetTy,
2231 newCall.getResult(), call.getResult(),
2232 slotBlock, dl, coercionOps,
2233 fc.returnInfo.directOffset)))
2234 return mlir::failure();
2237 if (needsArgAttrUpdate(fc)) {
2238 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
2239 newCall->setAttr(
"arg_attrs", updateArgAttrs(ctx, module, origCallArgTypes,
2244 if (fc.returnInfo.kind == ArgKind::Extend ||
2245 (hasResult && fc.returnInfo.kind == ArgKind::Direct)) {
2246 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
2247 if (mlir::ArrayAttr updated =
2248 updateResAttrs(ctx, existing, fc.returnInfo, origRetTy, dl))
2249 newCall->setAttr(
"res_attrs", updated);
2250 }
else if (hasResult && mlir::isa<cir::VoidType>(callRetTy)) {
2251 newCall->removeAttr(
"res_attrs");
2254 if (hasResult && fc.returnInfo.kind == ArgKind::Ignore) {
2259 if (!call.getResult().use_empty()) {
2260 builder.setInsertionPointAfter(newCall);
2261 mlir::Value poison =
2262 createIgnoredValue(builder, call.getLoc(), origRetTy);
2263 call.getResult().replaceAllUsesWith(poison);
2265 }
else if (hasResult && !returnNeedsCoercion) {
2267 call.getResult().replaceAllUsesWith(newCall.getResult());
2271 eraseDeadRecordLoads(deadRecordLoads);
2273 return mlir::success();
2278 mlir::OpBuilder &builder) {
2279 auto oldPtrTy = mlir::cast<cir::PointerType>(addrOp.getAddr().getType());
2280 cir::FuncType newFuncTy = funcOp.getFunctionType();
2283 if (newFuncTy == oldPtrTy.getPointee())
2287 addrOp.getAddr().setType(cir::PointerType::get(newFuncTy));
2288 if (addrOp.getAddr().use_empty())
2293 mlir::OpBuilder::InsertionGuard guard(builder);
2294 builder.setInsertionPointAfter(addrOp);
2295 auto bitcast = cir::CastOp::create(builder, addrOp.getLoc(), oldPtrTy,
2296 cir::CastKind::bitcast, addrOp.getAddr());
2297 addrOp.getAddr().replaceAllUsesExcept(bitcast.getResult(), bitcast);
2306 VAArgFetch(mlir::Location loc, mlir::Value valist,
2308 const ArgClassification &ac,
const mlir::DataLayout &dl,
2309 mlir::ModuleOp module)
2310 : loc(loc), valist(valist), vaFields(vaFields), resultTy(resultTy),
2311 ac(ac), dl(dl), module(module) {
2312 assert(vaFields.size() == 4 &&
2313 "the x86-64 va_list is a four-field cursor, checked by the caller");
2319 mlir::Type resultTy;
2320 const ArgClassification ∾
2321 const mlir::DataLayout &dl;
2322 mlir::ModuleOp module;
2328struct RegisterCursor {
2329 mlir::Value gpOffsetP;
2330 mlir::Value fpOffsetP;
2331 mlir::Value gpOffset;
2332 mlir::Value fpOffset;
2336mlir::LogicalResult reportVAArgNYI(cir::VAArgOp op, llvm::StringRef what) {
2337 op->emitOpError() <<
"va_arg of " << what
2338 <<
" not yet implemented in CallConvLowering";
2339 return mlir::failure();
2346mlir::LogicalResult classifyRegisterPair(cir::VAArgOp op,
2347 const ArgClassification &ac,
2348 std::array<bool, 2> &pairIsSse,
2350 auto pairTy = mlir::dyn_cast<cir::RecordType>(ac.coercedType);
2352 return mlir::success();
2354 if (pairTy.getNumElements() != 2)
2355 return reportVAArgNYI(op,
"a register coercion that is not two eightbytes");
2357 assert(!ac.directOffset &&
2358 "a pair already spans both eightbytes, so it cannot also start "
2359 "partway into the value");
2360 for (
auto [i, memberTy] : llvm::enumerate(pairTy.getMembers()))
2361 pairIsSse[i] = isSSERegisterClass(memberTy);
2363 return mlir::success();
2371uint64_t argumentAreaAlign(mlir::Type ty, mlir::ModuleOp modOp,
2372 const mlir::DataLayout &dl) {
2374 if (
auto recTy = mlir::dyn_cast<cir::RecordType>(ty))
2376 align = std::max<uint64_t>(align, layout.getRecordAlign());
2380mlir::Value roundPointerUpToAlignment(
CIRBaseBuilderTy &b, mlir::Location loc,
2381 mlir::Value bytePtr, uint64_t align,
2382 const mlir::DataLayout &dl) {
2383 assert(mlir::cast<cir::PointerType>(bytePtr.getType()).getPointee() ==
2385 "the bump strides in bytes, so the pointee must be u8");
2386 assert(llvm::isPowerOf2_64(align) &&
2387 "mask rounding needs a power-of-two alignment");
2388 mlir::Value bumped =
2390 std::optional<uint64_t> indexWidth =
2391 dl.getTypeIndexBitwidth(bytePtr.getType());
2392 assert(indexWidth &&
"a pointer in the argument area has an index width");
2394 static_cast<unsigned>(*indexWidth));
2395 return cir::PtrMaskOp::create(b, loc, bytePtr.getType(), bumped, mask);
2401 const VAArgFetch &f) {
2404 f.loc, b.
getPointerTo(f.vaFields[2]), f.valist,
"overflow_arg_area", 2);
2405 mlir::Value overflow = b.
createLoad(f.loc, overflowP);
2408 uint64_t tyAlign = argumentAreaAlign(f.resultTy, f.module, f.dl);
2410 bytePtr = roundPointerUpToAlignment(b, f.loc, bytePtr, tyAlign, f.dl);
2413 llvm::alignTo(f.dl.getTypeAllocSize(f.resultTy).getFixedValue(), 8);
2414 mlir::Value strideVal = b.
getSignedInt(f.loc, stride, 32);
2425 unsigned neededInt,
unsigned neededSse) {
2426 RegisterCursor cursor;
2429 f.valist,
"gp_offset", 0);
2430 cursor.gpOffset = b.
createLoad(f.loc, cursor.gpOffsetP);
2432 b.
getConstantInt(f.loc, cursor.gpOffset.getType(), 48 - neededInt * 8);
2434 b.
createCompare(f.loc, cir::CmpOpKind::le, cursor.gpOffset, limit);
2438 f.valist,
"fp_offset", 1);
2439 cursor.fpOffset = b.
createLoad(f.loc, cursor.fpOffsetP);
2440 mlir::Value limit = b.
getConstantInt(f.loc, cursor.fpOffset.getType(),
2441 176 - neededSse * 16);
2442 mlir::Value fitsInFp =
2443 b.
createCompare(f.loc, cir::CmpOpKind::le, cursor.fpOffset, limit);
2444 cursor.inRegs = cursor.inRegs
2454 const ArgClassification &ac,
2455 const RegisterCursor &cursor,
2456 const std::array<bool, 2> &pairIsSse,
2457 mlir::Value regSaveArea, mlir::Value regPairTemp) {
2458 auto pairTy = mlir::cast<cir::RecordType>(ac.coercedType);
2462 bool bothSse = pairIsSse[0] && pairIsSse[1];
2465 unsigned seenOfClass[2] = {0, 0};
2466 for (
unsigned i = 0; i < 2; ++i) {
2467 bool isSse = pairIsSse[i];
2468 mlir::Value base = isSse ? cursor.fpOffset : cursor.gpOffset;
2469 unsigned regSize = isSse ? 16 : 8;
2470 unsigned prior = seenOfClass[isSse];
2471 ++seenOfClass[isSse];
2472 mlir::Value off = base;
2478 mlir::Type elemTy = pairTy.getElementType(i);
2489uint64_t registerSlotSize(
unsigned neededInt,
unsigned neededSse) {
2490 assert(!(neededInt && neededSse) &&
2491 "a fetch needing both classes is a pair, reassembled elsewhere");
2492 return neededSse ? neededSse * 16 : neededInt * 8;
2497bool needsTempCopy(
const VAArgFetch &f,
unsigned neededInt,
2498 unsigned neededSse) {
2499 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
2500 if (f.ac.coercedType &&
2501 (f.ac.directOffset || registerSlotSize(neededInt, neededSse) < tySize))
2505 uint64_t slotAlign = neededSse ? 16 : 8;
2506 return argumentAreaAlign(f.resultTy, f.module, f.dl) > slotAlign;
2512 const VAArgFetch &f, mlir::Value regAddr,
2513 mlir::Value temp,
unsigned neededInt,
2514 unsigned neededSse) {
2516 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
2518 if (f.ac.coercedType &&
2519 (f.ac.directOffset || registerSlotSize(neededInt, neededSse) < tySize)) {
2526 mlir::Value dst = temp;
2527 if (f.ac.directOffset) {
2529 mlir::Value offset = b.
getSignedInt(loc, f.ac.directOffset, 32);
2537 if (mlir::isa<cir::RecordType>(f.resultTy))
2538 cir::CopyOp::create(
2539 b, loc, temp, regView,
2540 b.getI64IntegerAttr(argumentAreaAlign(f.resultTy, f.module, f.dl)),
2541 b.getI64IntegerAttr(8));
2548 const RegisterCursor &cursor,
unsigned neededInt,
2549 unsigned neededSse) {
2570 const ArgClassification &ac,
2571 mlir::OpBuilder &opBuilder) {
2572 auto op = mlir::cast<cir::VAArgOp>(vaArgOp);
2574 mlir::Location loc = op.getLoc();
2575 mlir::Type resultTy = op.getType();
2576 mlir::Value valist = op.getArgList();
2581 if (ac.kind == ArgKind::Ignore) {
2582 builder.setInsertionPoint(op);
2583 op.getResult().replaceAllUsesWith(
2584 createIgnoredValue(builder, loc, resultTy));
2586 return mlir::success();
2589 if (ac.kind == ArgKind::Indirect && !ac.byVal)
2590 return reportVAArgNYI(op,
"a non-trivially-copyable type");
2595 unsigned neededInt = ac.neededIntRegs;
2596 unsigned neededSse = ac.neededSseRegs;
2600 std::array<bool, 2> pairIsSse = {
false,
false};
2601 bool isRegPair =
false;
2602 if (ac.kind == ArgKind::Direct && neededInt + neededSse == 2 &&
2604 if (classifyRegisterPair(op, ac, pairIsSse, isRegPair).failed())
2605 return mlir::failure();
2608 auto vaListRecTy = mlir::dyn_cast<cir::RecordType>(
2609 mlir::cast<cir::PointerType>(valist.getType()).getPointee());
2610 if (!vaListRecTy || vaListRecTy.getNumElements() != 4) {
2611 return reportVAArgNYI(op,
2612 "a va_list that is not the four-field gp_offset / "
2613 "fp_offset / overflow_arg_area / reg_save_area "
2617 const VAArgFetch fetch{loc, valist, vaListRecTy.getMembers(), resultTy, ac,
2621 builder.setInsertionPoint(op);
2624 if (neededInt == 0 && neededSse == 0) {
2625 addr = buildOverflowAddrAndAdvance(builder, fetch);
2627 RegisterCursor cursor =
2628 buildRegisterGate(builder, fetch, neededInt, neededSse);
2636 bool pairNeedsReassembly = isRegPair && neededSse != 0;
2639 mlir::Value regPairTemp;
2640 if (pairNeedsReassembly) {
2645 loc, builder.
getPointerTo(ac.coercedType),
"vaarg.reg",
2647 std::max(dl.getTypeABIAlignment(ac.coercedType),
2648 argumentAreaAlign(resultTy, module, dl))));
2651 mlir::Value regTemp;
2652 bool copyThroughTemp =
2653 !pairNeedsReassembly && needsTempCopy(fetch, neededInt, neededSse);
2654 if (copyThroughTemp) {
2658 argumentAreaAlign(resultTy, module, dl)));
2661 addr = cir::TernaryOp::create(
2662 builder, loc, cursor.inRegs,
2664 [&](mlir::OpBuilder &ob, mlir::Location l) {
2665 CIRBaseBuilderTy b(ob);
2666 mlir::Value regSaveArea = b.createLoad(
2667 l, b.createGetMember(l, b.getPointerTo(fetch.vaFields[3]),
2668 valist,
"reg_save_area", 3));
2669 regSaveArea = b.createPtrBitcast(regSaveArea, byteTy);
2671 mlir::Value regAddr;
2672 if (pairNeedsReassembly) {
2673 reassembleRegisterPair(b, l, ac, cursor, pairIsSse,
2674 regSaveArea, regPairTemp);
2675 regAddr = b.createPtrBitcast(regPairTemp, byteTy);
2678 neededSse ? cursor.fpOffset : cursor.gpOffset;
2679 regAddr = b.createPtrStride(l, regSaveArea, off);
2680 if (copyThroughTemp) {
2681 regAddr = copyRegisterToTemp(b, l, fetch, regAddr, regTemp,
2682 neededInt, neededSse);
2686 advanceRegisterCursors(b, l, cursor, neededInt, neededSse);
2687 cir::YieldOp::create(b, l, regAddr);
2690 [&](mlir::OpBuilder &ob, mlir::Location l) {
2692 mlir::Value memAddr = buildOverflowAddrAndAdvance(b, fetch);
2693 cir::YieldOp::create(b, l, memAddr);
2699 mlir::Value resultAddr = builder.createPtrBitcast(addr, resultTy);
2700 uint64_t resultAlign = argumentAreaAlign(resultTy, module, dl);
2701 if (mlir::isa<cir::RecordType>(resultTy)) {
2702 SmallPtrSet<mlir::Operation *, 1> noExceptions;
2703 if (failed(rewireRecordUses(builder, loc, op.getResult(), resultAddr,
2704 resultAlign, dl, noExceptions)))
2705 return mlir::failure();
2707 op.getResult().replaceAllUsesWith(
2708 builder.createAlignedLoad(loc, resultAddr, resultAlign));
2711 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.
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
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...
bool memberOwnsBytes(mlir::Type memberTy)
Whether a record member occupies bytes of its record.
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.
U cast(CodeGen::Address addr)
__builtin_bitreverse32 uint32_t __builtin_riscv_aaddu_u32 __builtin_riscv_asubu_u32 __builtin_elementwise_add_sat __builtin_elementwise_sub_sat int32_t uint32_t uint32_t __builtin_riscv_mhacc_i32 __builtin_riscv_mhaccu_u32 __builtin_riscv_mhaccsu_i32 __builtin_riscv_mulq_i32 int64_t
static bool noaliasOnByvalAttr()