10#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
11#include "mlir/IR/Builders.h"
12#include "mlir/IR/Dominance.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/MathExtras.h"
26using namespace mlir::abi;
73 if (ac.kind != ArgKind::Direct || !ac.coercedType || !ac.canFlatten)
75 auto recTy = dyn_cast<cir::RecordType>(ac.coercedType);
76 if (!recTy || !recTy.isStruct() || recTy.getNumElements() <= 1)
88 const FunctionClassification &fc,
89 SmallVectorImpl<mlir::Type> &newArgTypes,
90 function_ref<mlir::InFlightDiagnostic()> emitError) {
91 assert(newArgTypes.empty() &&
"expected an empty output vector");
92 newArgTypes.reserve(oldArgTypes.size());
93 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
94 mlir::Type origTy = oldArgTypes[idx];
102 llvm::append_range(newArgTypes, flatTy.getMembers());
108 newArgTypes.push_back(ac.coercedType ? ac.coercedType : origTy);
111 case ArgKind::Ignore:
113 case ArgKind::Expand: {
117 auto recTy = cast<cir::RecordType>(origTy);
118 assert(recTy.isStruct() &&
119 "Expand classification requires a struct type, not a union");
120 assert(!recTy.getMembers().empty() &&
121 "Expand classification requires at least one struct field");
122 llvm::append_range(newArgTypes, recTy.getMembers());
125 case ArgKind::Extend:
132 newArgTypes.push_back(origTy);
134 case ArgKind::Indirect:
135 newArgTypes.push_back(cir::PointerType::get(origTy));
139 return mlir::success();
147computeNewReturnType(mlir::Type origRetTy,
const ArgClassification &retInfo,
148 mlir::MLIRContext *ctx,
149 function_ref<mlir::InFlightDiagnostic()> emitError) {
150 switch (retInfo.kind) {
151 case ArgKind::Direct:
154 return retInfo.coercedType ? retInfo.coercedType : origRetTy;
155 case ArgKind::Ignore:
156 return cir::VoidType::get(ctx);
157 case ArgKind::Expand:
158 emitError() <<
"Expand return is not allowed (classic codegen rejects "
159 <<
"it in EmitFunctionEpilog)";
161 case ArgKind::Extend:
166 case ArgKind::Indirect:
171 return cir::VoidType::get(ctx);
173 llvm_unreachable(
"all ArgKind cases handled");
181mlir::Value createIgnoredValue(mlir::OpBuilder &builder, mlir::Location loc,
183 return cir::ConstantOp::create(builder, loc, ty, cir::PoisonAttr::get(ty));
191mlir::ArrayAttr updateArgAttrs(mlir::MLIRContext *ctx,
193 mlir::ArrayAttr existingArgAttrs,
194 const FunctionClassification &fc,
195 const mlir::DataLayout &dl) {
196 mlir::Builder builder(ctx);
198 newArgAttrs.reserve(fc.argInfos.size());
199 for (
auto [oldIdx, ac] : llvm::enumerate(fc.argInfos)) {
200 if (ac.kind == ArgKind::Ignore)
202 mlir::DictionaryAttr existing = builder.getDictionaryAttr({});
203 if (existingArgAttrs && oldIdx < existingArgAttrs.size())
204 existing = mlir::cast<mlir::DictionaryAttr>(existingArgAttrs[oldIdx]);
208 newArgAttrs.append(flatTy.getNumElements(),
209 builder.getDictionaryAttr({}));
210 }
else if (ac.kind == ArgKind::Expand) {
213 auto recTy = cast<cir::RecordType>(origArgTypes[oldIdx]);
214 newArgAttrs.append(recTy.getNumElements(), builder.getDictionaryAttr({}));
215 }
else if (ac.kind == ArgKind::Extend) {
216 StringRef attrName = ac.signExtend
217 ? mlir::LLVM::LLVMDialect::getSExtAttrName()
218 :
mlir::LLVM::LLVMDialect::getZExtAttrName();
219 mlir::NamedAttrList attrs(existing);
220 attrs.set(attrName, builder.getUnitAttr());
221 newArgAttrs.push_back(attrs.getDictionary(ctx));
222 }
else if (ac.kind == ArgKind::Indirect) {
231 mlir::Type pointeeTy = origArgTypes[oldIdx];
232 mlir::NamedAttrList attrs(existing);
233 attrs.set(mlir::LLVM::LLVMDialect::getAlignAttrName(),
234 builder.getI64IntegerAttr(ac.indirectAlign.value()));
235 attrs.set(mlir::LLVM::LLVMDialect::getNoUndefAttrName(),
236 builder.getUnitAttr());
241 attrs.set(mlir::LLVM::LLVMDialect::getByValAttrName(),
242 mlir::TypeAttr::get(pointeeTy));
248 attrs.set(mlir::LLVM::LLVMDialect::getNoFreeObjAttrName(),
249 builder.getUnitAttr());
250 attrs.set(mlir::LLVM::LLVMDialect::getDereferenceableAttrName(),
251 builder.getI64IntegerAttr(
252 dl.getTypeSize(pointeeTy).getFixedValue()));
254 newArgAttrs.push_back(attrs.getDictionary(ctx));
256 newArgAttrs.push_back(existing);
259 return builder.getArrayAttr(newArgAttrs);
265mlir::ArrayAttr updateResAttrs(mlir::MLIRContext *ctx,
266 mlir::ArrayAttr existingResAttrs,
267 const ArgClassification &retInfo) {
268 if (retInfo.kind != ArgKind::Extend)
269 return existingResAttrs;
272 if (existingResAttrs && !existingResAttrs.empty())
273 for (mlir::NamedAttribute na :
274 mlir::cast<mlir::DictionaryAttr>(existingResAttrs[0]))
276 StringRef attrName = retInfo.signExtend ?
"llvm.signext" :
"llvm.zeroext";
277 attrs.push_back(mlir::NamedAttribute(mlir::StringAttr::get(ctx, attrName),
278 mlir::UnitAttr::get(ctx)));
279 return mlir::ArrayAttr::get(ctx, {mlir::DictionaryAttr::get(ctx, attrs)});
292static uint64_t coercionByteSize(mlir::Type ty,
const mlir::DataLayout &dl) {
293 if (
auto intTy = mlir::dyn_cast<cir::IntType>(ty))
294 return llvm::divideCeil(intTy.getWidth(), 8);
295 return dl.getTypeSize(ty);
328mlir::Value emitCoercionToMemory(mlir::OpBuilder &builder, mlir::Location loc,
329 mlir::Type dstTy, mlir::Value src,
330 mlir::Block *slotBlock,
331 const mlir::DataLayout &dl,
332 SmallPtrSetImpl<mlir::Operation *> &createdOps,
334 mlir::Type srcTy = src.getType();
335 assert(srcTy != dstTy &&
336 "emitCoercion callers must pre-check that the types differ");
338 uint64_t srcAlign = dl.getTypeABIAlignment(srcTy);
339 uint64_t dstAlign = dl.getTypeABIAlignment(dstTy);
340 uint64_t allocaAlign = std::max(srcAlign, dstAlign);
341 mlir::Type slotTy = coercionByteSize(srcTy, dl) >= coercionByteSize(dstTy, dl)
348 [[maybe_unused]] mlir::Type coercedTy = ((slotTy == srcTy) ? dstTy : srcTy);
349 assert((offset == 0 ||
350 coercionByteSize(coercedTy, dl) < coercionByteSize(slotTy, dl)) &&
351 "a direct offset must land on the coerced side, the smaller one");
352 assert((offset == 0 ||
353 offset + dl.getTypeSize(coercedTy) <= dl.getTypeSize(slotTy)) &&
354 "coerce slot too small for offset access");
355 assert((offset == 0 || offset % dl.getTypeABIAlignment(coercedTy) == 0) &&
356 "a direct offset must be aligned for the coerced access");
358 auto slotPtrTy = cir::PointerType::get(slotTy);
359 auto srcPtrTy = cir::PointerType::get(srcTy);
360 auto dstPtrTy = cir::PointerType::get(dstTy);
362 cir::AllocaOp alloca;
364 mlir::OpBuilder::InsertionGuard guard(builder);
365 builder.setInsertionPointToStart(slotBlock);
366 alloca = cir::AllocaOp::create(builder, loc, slotPtrTy,
367 builder.getStringAttr(
"coerce"),
368 builder.getI64IntegerAttr(allocaAlign));
370 createdOps.insert(alloca);
375 auto slotView = [&](mlir::Type wantTy,
376 cir::PointerType wantPtrTy) -> mlir::Value {
377 if (wantTy == slotTy)
379 mlir::Value base = alloca;
382 cir::IntType::get(builder.getContext(), 8,
false);
383 auto u8PtrTy = cir::PointerType::get(u8Ty);
384 auto u8Base = cir::CastOp::create(builder, loc, u8PtrTy,
385 cir::CastKind::bitcast, alloca);
386 createdOps.insert(u8Base);
388 cir::IntType::get(builder.getContext(), 64,
true);
389 auto strideVal = cir::ConstantOp::create(
390 builder, loc, cir::IntAttr::get(strideTy, offset));
391 createdOps.insert(strideVal);
392 base = cir::PtrStrideOp::create(builder, loc, u8PtrTy, u8Base, strideVal);
393 createdOps.insert(base.getDefiningOp());
395 auto cast = cir::CastOp::create(builder, loc, wantPtrTy,
396 cir::CastKind::bitcast, base);
397 createdOps.insert(cast);
402 mlir::Value srcSlot = slotView(srcTy, srcPtrTy);
403 auto store = cir::StoreOp::create(builder, loc, src, srcSlot);
404 createdOps.insert(store);
407 return slotView(dstTy, dstPtrTy);
413mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
414 mlir::Type dstTy, mlir::Value src,
415 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
416 SmallPtrSetImpl<mlir::Operation *> &createdOps,
418 mlir::Value dstSlot = emitCoercionToMemory(builder, loc, dstTy, src,
419 slotBlock, dl, createdOps, offset);
420 auto load = cir::LoadOp::create(builder, loc, dstSlot);
421 createdOps.insert(load);
427mlir::Value emitCoercion(mlir::OpBuilder &builder, mlir::Location loc,
428 mlir::Type dstTy, mlir::Value src,
429 mlir::Block *slotBlock,
const mlir::DataLayout &dl,
431 SmallPtrSet<mlir::Operation *, 4> ignored;
432 return emitCoercion(builder, loc, dstTy, src, slotBlock, dl, ignored, offset);
444mlir::Block *coercionSlotBlock(mlir::Operation *op) {
445 if (
auto funcOp = op->getParentOfType<mlir::FunctionOpInterface>())
446 return &funcOp->getRegion(0).front();
447 mlir::Region *region = op->getParentRegion();
448 while (mlir::Region *outer = region->getParentRegion()) {
449 if (mlir::isa<mlir::ModuleOp>(outer->getParentOp()))
453 assert(!region->empty() &&
"coercion slot needs a block to hold the alloca");
454 return ®ion->front();
459void insertReturnCoercion(mlir::FunctionOpInterface funcOp,
460 mlir::Type origRetTy, mlir::Type coercedRetTy,
461 mlir::OpBuilder &builder,
const mlir::DataLayout &dl,
464 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
465 for (cir::ReturnOp r : returns) {
466 if (r.getInput().empty())
468 mlir::Value origVal = r.getInput()[0];
469 if (origVal.getType() == coercedRetTy)
471 builder.setInsertionPoint(r);
472 mlir::Value coerced =
473 emitCoercion(builder, r.getLoc(), coercedRetTy, origVal,
474 &funcOp->getRegion(0).front(), dl, offset);
475 r->setOperand(0, coerced);
483static cir::LoadOp maybeGetSimpleLoad(mlir::Value val) {
484 cir::LoadOp load = val.getDefiningOp<cir::LoadOp>();
485 if (!load || load.getIsVolatile() || load.getMemOrder())
492static cir::LoadOp getWholeRecordLoad(mlir::Value recordVal) {
493 cir::LoadOp load = maybeGetSimpleLoad(recordVal);
510static bool forwardableNonByvalStorage(mlir::Value addr, uint64_t minAlign) {
512 return slot && slot.getAlignment() >= minAlign;
526static void emitStructFieldArgs(mlir::OpBuilder &builder, mlir::Location loc,
528 SmallVectorImpl<mlir::Value> &newArgs,
529 SmallVectorImpl<cir::LoadOp> &deadRecordLoads) {
530 cir::LoadOp srcLoad = getWholeRecordLoad(structVal);
533 mlir::OpBuilder::InsertionGuard guard(builder);
534 builder.setInsertionPoint(srcLoad);
535 cir::PointerType baseTy = srcLoad.getAddr().getType();
536 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
537 mlir::Type fieldPtrTy =
538 cir::PointerType::get(fieldTy, baseTy.getAddrSpace());
539 mlir::Value fieldPtr = cir::GetMemberOp::create(
540 builder, loc, fieldPtrTy, srcLoad.getAddr(),
"",
542 newArgs.push_back(cir::LoadOp::create(builder, loc, fieldPtr));
544 deadRecordLoads.push_back(srcLoad);
548 cir::ExtractMemberOp::create(builder, loc, structVal, f));
559 for (mlir::Operation *load : uniqueLoads)
560 if (load->use_empty())
570static std::pair<cir::StoreOp, cir::AllocaOp>
571findParamSpill(mlir::BlockArgument blockArg) {
572 if (blockArg.use_empty())
574 assert(blockArg.hasOneUse() &&
575 "non-byval arg must have exactly one use (the CIRGen param spill)");
576 auto store = cast<cir::StoreOp>(*blockArg.user_begin());
577 assert(store.getValue() == blockArg &&
578 "non-byval arg's use must be the value operand of its store");
579 return {store, cast<cir::AllocaOp>(store.getAddr().getDefiningOp())};
599void insertArgCoercion(
600 mlir::FunctionOpInterface funcOp,
const FunctionClassification &fc,
601 mlir::OpBuilder &builder,
const mlir::DataLayout &dl,
bool hasSRetArg,
602 SmallVectorImpl<std::pair<cir::AllocaOp, mlir::BlockArgument>>
603 &pendingParamSlots) {
604 mlir::Region &body = funcOp->getRegion(0);
607 mlir::Block &entry = body.front();
613 unsigned blockArgIdx = hasSRetArg ? 1 : 0;
615 for (
const ArgClassification &ac : fc.argInfos) {
616 assert(blockArgIdx < entry.getNumArguments() &&
617 "classification count must not exceed entry block arguments");
619 if (ac.kind == ArgKind::Expand) {
623 mlir::BlockArgument origArg = entry.getArgument(blockArgIdx);
624 auto recTy = cast<cir::RecordType>(origArg.getType());
626 "Expand classification requires a struct type, not a union");
628 assert(numFields > 0 &&
629 "Expand classification requires at least one struct field");
630 mlir::Location loc = funcOp.getLoc();
640 cir::StoreOp paramStore;
641 cir::AllocaOp destAlloca;
642 if (!origArg.use_empty()) {
643 assert(origArg.hasOneUse() &&
644 "Expand arg must have exactly one use (the CIRGen param spill)");
645 paramStore = cast<cir::StoreOp>(*origArg.user_begin());
646 assert(paramStore.getValue() == origArg &&
647 "Expand arg's use must be the value operand of its store");
648 destAlloca = cast<cir::AllocaOp>(paramStore.getAddr().getDefiningOp());
655 mlir::Operation *fieldStoreInsertPt =
nullptr;
657 fieldStoreInsertPt = paramStore->getNextNode();
658 assert(fieldStoreInsertPt &&
659 "param spill must be followed by a block terminator");
671 builder.setInsertionPoint(fieldStoreInsertPt);
672 for (
auto [f, fieldTy] : llvm::enumerate(recTy.
getMembers())) {
674 origArg.setType(fieldTy);
676 entry.insertArgument(blockArgIdx + f, fieldTy, loc);
679 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
680 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
683 cir::StoreOp::create(builder, loc, entry.getArgument(blockArgIdx + f),
687 blockArgIdx += numFields;
691 mlir::BlockArgument blockArg = entry.getArgument(blockArgIdx);
700 unsigned numFields = flatTy.getNumElements();
701 assert(numFields >= 2 &&
"getFlattenedCoercedType guarantees >1 fields");
702 Type origTy = blockArg.getType();
703 Location loc = funcOp.getLoc();
706 blockArg.setType(flatTy.getElementType(0));
707 for (
unsigned f = 1; f < numFields; ++f)
708 entry.insertArgument(blockArgIdx + f, flatTy.getElementType(f), loc);
711 builder.setInsertionPointToStart(&entry);
712 auto flatPtrTy = cir::PointerType::get(flatTy);
713 uint64_t flatAlign = dl.getTypeABIAlignment(flatTy);
714 auto flatSlot = cir::AllocaOp::create(
715 builder, loc, flatPtrTy, builder.getStringAttr(
"coerce"),
716 builder.getI64IntegerAttr(flatAlign));
717 SmallPtrSet<Operation *, 8> flattenOps = {flatSlot};
718 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
719 Type fieldPtrTy = cir::PointerType::get(fieldTy);
720 auto fieldPtr = cir::GetMemberOp::create(builder, loc, fieldPtrTy,
723 flattenOps.insert(fieldPtr);
724 auto storeOp = cir::StoreOp::create(
725 builder, loc, entry.getArgument(blockArgIdx + f), fieldPtr);
726 flattenOps.insert(storeOp);
729 cir::LoadOp::create(builder, loc, flatTy, flatSlot.getResult());
730 flattenOps.insert(flatLoaded);
734 Value finalVal = flatLoaded;
735 if (origTy != flatTy) {
736 SmallPtrSet<Operation *, 4> coercionOps;
737 assert(!ac.directOffset &&
738 "each field is read from slot offset 0 here, so a flattened "
739 "coercion cannot honor a direct offset");
740 finalVal = emitCoercion(builder, loc, origTy, flatLoaded, &entry, dl,
742 flattenOps.insert(coercionOps.begin(), coercionOps.end());
747 blockArg.replaceAllUsesExcept(finalVal, flattenOps);
749 blockArgIdx += numFields;
753 if (ac.kind == ArgKind::Direct && ac.coercedType) {
754 mlir::Type oldArgTy = blockArg.getType();
755 mlir::Type newArgTy = ac.coercedType;
756 if (oldArgTy == newArgTy) {
760 blockArg.setType(newArgTy);
762 builder.setInsertionPointToStart(&entry);
763 SmallPtrSet<mlir::Operation *, 4> coercionOps;
764 mlir::Value adapted =
765 emitCoercion(builder, funcOp.getLoc(), oldArgTy, blockArg, &entry, dl,
766 coercionOps, ac.directOffset);
772 blockArg.replaceAllUsesExcept(adapted, coercionOps);
773 }
else if (ac.kind == ArgKind::Indirect) {
778 auto ptrTy = cir::PointerType::get(blockArg.getType());
786 auto [paramStore, destAlloca] = findParamSpill(blockArg);
792 blockArg.setType(ptrTy);
801 pendingParamSlots.emplace_back(destAlloca, blockArg);
805 blockArg.setType(ptrTy);
807 builder.setInsertionPointToStart(&entry);
808 auto loadOp = cir::LoadOp::create(builder, funcOp.getLoc(), blockArg);
809 SmallPtrSet<mlir::Operation *, 1> loadOps = {loadOp};
810 blockArg.replaceAllUsesExcept(loadOp.getResult(), loadOps);
847void insertSRetStores(mlir::FunctionOpInterface funcOp, mlir::Type origRetTy,
848 mlir::OpBuilder &builder) {
849 mlir::Value sretPtr = funcOp.getArguments()[0];
852 funcOp->walk([&](cir::ReturnOp retOp) { returnOps.push_back(retOp); });
854 cir::AllocaOp retAlloca =
nullptr;
855 for (cir::ReturnOp retOp : returnOps) {
858 assert(!retOp.getInput().empty() &&
859 "cir.return in sret function must have an operand");
861 cir::LoadOp retLoad =
862 mlir::cast<cir::LoadOp>(retOp.getInput()[0].getDefiningOp());
870 retAlloca = mlir::cast<cir::AllocaOp>(retLoad.getAddr().getDefiningOp());
871 retAlloca.getResult().replaceAllUsesWith(sretPtr);
877 builder.setInsertionPoint(retOp);
878 cir::ReturnOp::create(builder, retOp.getLoc());
880 if (retLoad.use_empty())
899 builder.getNamedAttr(
"llvm.sret", mlir::TypeAttr::get(retTy)));
901 builder.getNamedAttr(
"llvm.align", builder.getI64IntegerAttr(align)));
904 builder.getNamedAttr(
"llvm.noalias", builder.getUnitAttr()));
905 attrs.push_back(builder.getNamedAttr(
"llvm.writable", builder.getUnitAttr()));
907 builder.getNamedAttr(
"llvm.dead_on_unwind", builder.getUnitAttr()));
917void applySretSlotAttrs(cir::CallOp newCall, mlir::ArrayAttr argAttrs,
918 mlir::Type retTy, uint64_t align,
919 mlir::OpBuilder &builder) {
920 mlir::MLIRContext *ctx = newCall->getContext();
922 buildSretSlotAttrs(builder, retTy, align,
false);
925 newArgAttrs.reserve(newCall.getArgOperands().size());
926 newArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
928 llvm::append_range(newArgAttrs, argAttrs);
929 assert(newArgAttrs.size() <= newCall.getArgOperands().size() &&
930 "arg_attrs wider than the rewritten call's operand list");
931 newArgAttrs.resize(newCall.getArgOperands().size(),
932 mlir::DictionaryAttr::get(ctx));
933 newCall->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, newArgAttrs));
940static void prependIndirectCallee(cir::CallOp call,
941 SmallVectorImpl<mlir::Value> &args,
942 mlir::Type retTy, mlir::OpBuilder &builder) {
943 if (!call.isIndirect())
945 mlir::Value calleePtr = call.getIndirectCall();
947 paramTypes.reserve(args.size());
948 llvm::transform(args, std::back_inserter(paramTypes),
949 [](mlir::Value v) {
return v.getType(); });
958 auto calleeFnTy = cast<cir::FuncType>(
959 cast<cir::PointerType>(calleePtr.getType()).getPointee());
960 auto newPtrTy = cir::PointerType::get(
961 cir::FuncType::get(paramTypes, retTy, calleeFnTy.isVarArg()));
962 if (calleePtr.getType() != newPtrTy)
963 calleePtr = cir::CastOp::create(builder, call.getLoc(), newPtrTy,
964 cir::CastKind::bitcast, calleePtr);
965 args.insert(args.begin(), calleePtr);
975void rewriteIndirectReturnCall(cir::CallOp call,
976 const FunctionClassification &fc,
978 mlir::Type origRetTy,
980 mlir::OpBuilder &builder,
981 const mlir::DataLayout &dl) {
982 mlir::MLIRContext *ctx = call->getContext();
983 auto ptrTy = cir::PointerType::get(origRetTy);
984 builder.setInsertionPoint(call);
985 uint64_t sretAlign = fc.returnInfo.indirectAlign.value();
998 mlir::Value sretSlot =
nullptr;
999 cir::StoreOp reuseStore =
nullptr;
1000 if (call.getResult().hasOneUse()) {
1001 mlir::Operation *user = *call.getResult().getUsers().begin();
1002 if (
auto store = mlir::dyn_cast<cir::StoreOp>(user))
1003 if (store.getValue() == call.getResult() &&
1004 store.getAddr().getType() == ptrTy &&
1005 mlir::DominanceInfo().properlyDominates(store.getAddr(), call)) {
1006 sretSlot = store.getAddr();
1011 auto alloca = cir::AllocaOp::create(
1012 builder, call.getLoc(), ptrTy,
1013 builder.getStringAttr(
"sret"),
1014 builder.getI64IntegerAttr(sretAlign));
1019 sretArgs.push_back(sretSlot);
1020 sretArgs.append(newArgs.begin(), newArgs.end());
1022 mlir::Type sretVoidTy = cir::VoidType::get(ctx);
1023 prependIndirectCallee(call, sretArgs, sretVoidTy, builder);
1024 auto newCall = cir::CallOp::create(
1025 builder, call.getLoc(), call.getCalleeAttr(), sretVoidTy, sretArgs);
1026 for (mlir::NamedAttribute attr : call->getAttrs())
1027 if (!newCall->hasAttr(
attr.getName()))
1028 newCall->setAttr(
attr.getName(),
attr.getValue());
1029 newCall->removeAttr(
"res_attrs");
1036 mlir::ArrayAttr argAttrs = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1037 bool needsArgAttrUpdate =
1038 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1039 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1040 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1041 getFlattenedCoercedType(ac);
1043 if (needsArgAttrUpdate)
1044 argAttrs = updateArgAttrs(ctx, origCallArgTypes, argAttrs, fc, dl);
1045 applySretSlotAttrs(newCall, argAttrs, origRetTy, sretAlign, builder);
1051 reuseStore->erase();
1053 builder.setInsertionPointAfter(newCall);
1054 auto load = cir::LoadOp::create(builder, call.getLoc(), origRetTy, sretSlot,
1058 mlir::IntegerAttr(),
1059 cir::SyncScopeKindAttr(),
1060 cir::MemOrderAttr(),
1062 call.getResult().replaceAllUsesWith(load);
1069bool isSSERegisterClass(mlir::Type ty) {
1070 return mlir::isa<cir::VectorType, cir::FPTypeInterface>(ty);
1076 cir::FuncOp funcOp,
const FunctionClassification &fc) {
1077 if (!funcOp.isDefinition())
1079 mlir::Region &body = funcOp->getRegion(0);
1082 mlir::Block &entry = body.front();
1088 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1089 if (ac.kind != ArgKind::Indirect || ac.byVal)
1091 assert(idx < entry.getNumArguments() &&
1092 "classification count must not exceed entry block arguments");
1093 if (cir::AllocaOp slot = findParamSpill(entry.getArgument(idx)).second)
1094 slot.setAlignment(ac.indirectAlign.value());
1099 for (
auto [slot, incoming] : pendingParamSlots) {
1100 slot.getResult().replaceAllUsesWith(incoming);
1103 pendingParamSlots.clear();
1107 mlir::FunctionOpInterface funcOpInterface,
const FunctionClassification &fc,
1108 mlir::OpBuilder &builder) {
1114 cir::FuncOp funcOp = mlir::cast<cir::FuncOp>(funcOpInterface);
1116 if (!fc.needsRewrite())
1117 return mlir::success();
1121 mlir::MLIRContext *ctx = funcOp->getContext();
1126 assert(oldResultTypes.size() <= 1 &&
1127 "CIR functions return zero or one value");
1130 if (mlir::failed(buildNewArgTypes(oldArgTypes, fc, newArgTypes,
1131 [&]() {
return funcOp.emitOpError(); })))
1132 return mlir::failure();
1134 mlir::Type voidTy = cir::VoidType::get(ctx);
1135 mlir::Type origRetTy = oldResultTypes.empty() ? voidTy : oldResultTypes[0];
1136 mlir::Type newRetTy = computeNewReturnType(
1137 origRetTy, fc.returnInfo, ctx, [&]() { return funcOp.emitOpError(); });
1139 return mlir::failure();
1149 fc.returnInfo.kind == ArgKind::Indirect && !oldResultTypes.empty();
1151 newArgTypes.insert(newArgTypes.begin(), cir::PointerType::get(origRetTy));
1153 if (funcOp.isDefinition()) {
1154 mlir::Region &body = funcOp->getRegion(0);
1155 if (!body.empty()) {
1160 body.front().insertArgument(0u, cir::PointerType::get(origRetTy),
1162 insertSRetStores(funcOp, origRetTy, builder);
1172 insertArgCoercion(funcOp, fc, builder, dl, hasSRet, pendingParamSlots);
1177 if (fc.returnInfo.kind == ArgKind::Direct && fc.returnInfo.coercedType &&
1178 !oldResultTypes.empty() && fc.returnInfo.coercedType != origRetTy)
1179 insertReturnCoercion(funcOp, origRetTy, fc.returnInfo.coercedType,
1180 builder, dl, fc.returnInfo.directOffset);
1182 mlir::Block &entry = body.front();
1191 unsigned blockArgIdx = hasSRet ? 1 : 0;
1192 for (
auto [i, ac] : llvm::enumerate(fc.argInfos)) {
1193 if (blockArgIdx >= entry.getNumArguments())
1195 if (ac.kind == ArgKind::Ignore) {
1196 mlir::BlockArgument arg = entry.getArgument(blockArgIdx);
1197 if (!arg.use_empty()) {
1198 builder.setInsertionPointToStart(&entry);
1199 mlir::Value poison =
1200 createIgnoredValue(builder, funcOp.getLoc(), arg.getType());
1201 arg.replaceAllUsesWith(poison);
1203 entry.eraseArgument(blockArgIdx);
1207 blockArgIdx += flatTy.getNumElements();
1208 else if (ac.kind == ArgKind::Expand)
1209 blockArgIdx += cast<cir::RecordType>(oldArgTypes[i]).getNumElements();
1221 if (fc.returnInfo.kind == ArgKind::Ignore && !oldResultTypes.empty()) {
1222 assert(mlir::isa<cir::VoidType>(newRetTy) &&
1223 "Ignore-return path requires the new return type to be void");
1225 funcOp.walk([&](cir::ReturnOp r) { returns.push_back(r); });
1226 for (cir::ReturnOp r : returns) {
1227 if (r.getNumOperands() == 0)
1229 builder.setInsertionPoint(r);
1230 cir::ReturnOp::create(builder, r.getLoc());
1236 mlir::Type newFnTy = funcOp.cloneTypeWith(newArgTypes, newResultTypes);
1237 funcOp.setFunctionTypeAttr(mlir::TypeAttr::get(newFnTy));
1244 bool needsArgAttrUpdate =
1245 hasSRet || llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1246 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1247 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1248 getFlattenedCoercedType(ac);
1250 if (needsArgAttrUpdate) {
1251 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1252 mlir::ArrayAttr updated =
1253 updateArgAttrs(ctx, oldArgTypes, existing, fc, dl);
1259 builder, origRetTy, fc.returnInfo.indirectAlign.value(),
1260 funcOp.isDefinition());
1262 withSret.push_back(mlir::DictionaryAttr::get(ctx, sretAttrs));
1263 llvm::append_range(withSret, updated);
1264 funcOp->setAttr(
"arg_attrs", mlir::ArrayAttr::get(ctx, withSret));
1266 funcOp->setAttr(
"arg_attrs", updated);
1270 if (mlir::isa<cir::VoidType>(newRetTy)) {
1271 funcOp->removeAttr(
"res_attrs");
1272 }
else if (fc.returnInfo.kind == ArgKind::Extend) {
1274 auto existing = funcOp->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1275 funcOp->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1278 return mlir::success();
1283 const FunctionClassification &fc,
1284 mlir::OpBuilder &builder) {
1294 unsigned numOperands =
1295 mlir::cast<cir::CIRCallOpInterface>(callOp).getNumArgOperands();
1296 if (numOperands > fc.argInfos.size())
1297 return callOp->emitOpError()
1298 <<
"variadic arguments not yet implemented in CallConvLowering";
1299 if (numOperands < fc.argInfos.size())
1300 return callOp->emitOpError()
1301 <<
"call passes fewer arguments than the callee declares, which is "
1302 "not yet implemented in CallConvLowering";
1304 if (!fc.needsRewrite())
1305 return mlir::success();
1307 if (mlir::isa<cir::TryCallOp>(callOp))
1308 return callOp->emitOpError()
1309 <<
"TryCallOp not yet implemented in CallConvLowering";
1311 auto call = mlir::cast<cir::CallOp>(callOp);
1312 mlir::MLIRContext *ctx = callOp->getContext();
1313 mlir::Block *slotBlock = coercionSlotBlock(call);
1315 builder.setInsertionPoint(call);
1318 mlir::ValueRange argOperands = call.getArgOperands();
1319 newArgs.reserve(argOperands.size());
1331 llvm::append_range(origCallArgTypes, argOperands.getTypes());
1332 for (
auto [idx, ac] : llvm::enumerate(fc.argInfos)) {
1333 if (ac.kind == ArgKind::Ignore)
1335 mlir::Value arg = argOperands[idx];
1343 if (arg.getType() != flatTy) {
1344 SmallPtrSet<mlir::Operation *, 4> coercionOps;
1345 assert(!ac.directOffset &&
1346 "each field is read from slot offset 0 here, so a flattened "
1347 "coercion cannot honor a direct offset");
1348 mlir::Value coercedPtr =
1349 emitCoercionToMemory(builder, call.getLoc(), flatTy, arg, slotBlock,
1350 dl, coercionOps, 0);
1351 for (
auto [f, fieldTy] : llvm::enumerate(flatTy.getMembers())) {
1352 mlir::Type fieldPtrTy = cir::PointerType::get(fieldTy);
1354 cir::GetMemberOp::create(builder, call.getLoc(), fieldPtrTy,
1356 newArgs.push_back(cir::LoadOp::create(builder, call.getLoc(), fieldTy,
1357 fieldPtr.getResult()));
1360 emitStructFieldArgs(builder, call.getLoc(), arg, flatTy, newArgs,
1363 }
else if (ac.kind == ArgKind::Expand) {
1366 auto recTy = cast<cir::RecordType>(arg.getType());
1368 "Expand classification requires a struct type, not a union");
1369 emitStructFieldArgs(builder, call.getLoc(), arg, recTy, newArgs,
1371 }
else if (ac.kind == ArgKind::Direct && ac.coercedType &&
1372 arg.getType() != ac.coercedType) {
1373 arg = emitCoercion(builder, call.getLoc(), ac.coercedType, arg, slotBlock,
1374 dl, ac.directOffset);
1375 newArgs.push_back(arg);
1376 }
else if (ac.kind == ArgKind::Indirect) {
1387 cir::LoadOp srcLoad = maybeGetSimpleLoad(arg);
1389 srcLoad.getAddr().getType() !=
1390 cir::PointerType::get(arg.getType()) ||
1391 !forwardableNonByvalStorage(srcLoad.getAddr(),
1392 ac.indirectAlign.value()))
1393 return call->emitOpError()
1394 <<
"non-byval indirect argument that does not name the "
1395 "caller's storage is not yet implemented in "
1397 newArgs.push_back(srcLoad.getAddr());
1398 deadRecordLoads.push_back(srcLoad);
1401 auto ptrTy = cir::PointerType::get(arg.getType());
1402 auto slot = cir::AllocaOp::create(
1403 builder, call.getLoc(), ptrTy, builder.getStringAttr(
"byval"),
1404 builder.getI64IntegerAttr(ac.indirectAlign.value()));
1405 cir::StoreOp::create(builder, call.getLoc(), arg, slot);
1406 newArgs.push_back(slot);
1408 newArgs.push_back(arg);
1412 bool hasResult = call.getNumResults() > 0;
1413 mlir::Type origRetTy =
1414 hasResult ? call.getResult().getType() : cir::VoidType::get(ctx);
1420 if (fc.returnInfo.kind == ArgKind::Indirect && hasResult) {
1421 rewriteIndirectReturnCall(call, fc, newArgs, origRetTy, origCallArgTypes,
1423 eraseDeadRecordLoads(deadRecordLoads);
1424 return mlir::success();
1427 mlir::Type callRetTy = origRetTy;
1428 if (fc.returnInfo.kind == ArgKind::Ignore && hasResult)
1429 callRetTy = cir::VoidType::get(ctx);
1430 bool returnNeedsCoercion =
1431 hasResult && fc.returnInfo.kind == ArgKind::Direct &&
1432 fc.returnInfo.coercedType && fc.returnInfo.coercedType != origRetTy;
1433 if (returnNeedsCoercion)
1434 callRetTy = fc.returnInfo.coercedType;
1436 builder.setInsertionPoint(call);
1437 prependIndirectCallee(call, newArgs, callRetTy, builder);
1438 auto newCall = cir::CallOp::create(builder, call.getLoc(),
1439 call.getCalleeAttr(), callRetTy, newArgs);
1440 for (mlir::NamedAttribute attr : call->getAttrs())
1441 if (!newCall->hasAttr(attr.getName()))
1442 newCall->setAttr(attr.getName(), attr.getValue());
1446 if (returnNeedsCoercion) {
1447 builder.setInsertionPointAfter(newCall);
1448 mlir::Value coercedBack =
1449 emitCoercion(builder, call.getLoc(), origRetTy, newCall.getResult(),
1450 slotBlock, dl, fc.returnInfo.directOffset);
1451 call.getResult().replaceAllUsesWith(coercedBack);
1458 bool needsArgAttrUpdate =
1459 llvm::any_of(fc.argInfos, [](
const ArgClassification &ac) {
1460 return ac.kind == ArgKind::Ignore || ac.kind == ArgKind::Extend ||
1461 ac.kind == ArgKind::Indirect || ac.kind == ArgKind::Expand ||
1462 getFlattenedCoercedType(ac);
1464 if (needsArgAttrUpdate) {
1465 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"arg_attrs");
1466 newCall->setAttr(
"arg_attrs",
1467 updateArgAttrs(ctx, origCallArgTypes, existing, fc, dl));
1469 if (fc.returnInfo.kind == ArgKind::Extend) {
1470 auto existing = call->getAttrOfType<mlir::ArrayAttr>(
"res_attrs");
1471 newCall->setAttr(
"res_attrs", updateResAttrs(ctx, existing, fc.returnInfo));
1472 }
else if (hasResult && mlir::isa<cir::VoidType>(callRetTy)) {
1473 newCall->removeAttr(
"res_attrs");
1476 if (hasResult && fc.returnInfo.kind == ArgKind::Ignore) {
1481 if (!call.getResult().use_empty()) {
1482 builder.setInsertionPointAfter(newCall);
1483 mlir::Value poison =
1484 createIgnoredValue(builder, call.getLoc(), origRetTy);
1485 call.getResult().replaceAllUsesWith(poison);
1487 }
else if (hasResult && !returnNeedsCoercion) {
1489 call.getResult().replaceAllUsesWith(newCall.getResult());
1493 eraseDeadRecordLoads(deadRecordLoads);
1495 return mlir::success();
1500 mlir::OpBuilder &builder) {
1501 auto oldPtrTy = mlir::cast<cir::PointerType>(addrOp.getAddr().getType());
1502 cir::FuncType newFuncTy = funcOp.getFunctionType();
1505 if (newFuncTy == oldPtrTy.getPointee())
1509 addrOp.getAddr().setType(cir::PointerType::get(newFuncTy));
1510 if (addrOp.getAddr().use_empty())
1517 mlir::OpBuilder::InsertionGuard guard(builder);
1518 builder.setInsertionPointAfter(addrOp);
1519 auto bitcast = cir::CastOp::create(builder, addrOp.getLoc(), oldPtrTy,
1520 cir::CastKind::bitcast, addrOp.getAddr());
1521 addrOp.getAddr().replaceAllUsesExcept(bitcast.getResult(), bitcast);
1530 VAArgFetch(mlir::Location loc, mlir::Value valist,
1532 const ArgClassification &ac,
const mlir::DataLayout &dl,
1533 mlir::ModuleOp module)
1534 : loc(loc), valist(valist), vaFields(vaFields), resultTy(resultTy),
1535 ac(ac), dl(dl), module(module) {
1536 assert(vaFields.size() == 4 &&
1537 "the x86-64 va_list is a four-field cursor, checked by the caller");
1543 mlir::Type resultTy;
1544 const ArgClassification ∾
1545 const mlir::DataLayout &dl;
1546 mlir::ModuleOp module;
1552struct RegisterCursor {
1553 mlir::Value gpOffsetP;
1554 mlir::Value fpOffsetP;
1555 mlir::Value gpOffset;
1556 mlir::Value fpOffset;
1560mlir::LogicalResult reportVAArgNYI(cir::VAArgOp op, llvm::StringRef what) {
1561 op->emitOpError() <<
"va_arg of " << what
1562 <<
" not yet implemented in CallConvLowering";
1563 return mlir::failure();
1570mlir::LogicalResult classifyRegisterPair(cir::VAArgOp op,
1571 const ArgClassification &ac,
1572 std::array<bool, 2> &pairIsSse,
1574 auto pairTy = mlir::dyn_cast<cir::RecordType>(ac.coercedType);
1576 return mlir::success();
1578 if (pairTy.getNumElements() != 2)
1579 return reportVAArgNYI(op,
"a register coercion that is not two eightbytes");
1581 assert(!ac.directOffset &&
1582 "a pair already spans both eightbytes, so it cannot also start "
1583 "partway into the value");
1584 for (
auto [i, memberTy] : llvm::enumerate(pairTy.getMembers()))
1585 pairIsSse[i] = isSSERegisterClass(memberTy);
1587 return mlir::success();
1595uint64_t argumentAreaAlign(mlir::Type ty, mlir::ModuleOp modOp,
1596 const mlir::DataLayout &dl) {
1598 if (
auto recTy = mlir::dyn_cast<cir::RecordType>(ty))
1600 align = std::max<uint64_t>(align, layout.getRecordAlign());
1604mlir::Value roundPointerUpToAlignment(
CIRBaseBuilderTy &b, mlir::Location loc,
1605 mlir::Value bytePtr, uint64_t align,
1606 const mlir::DataLayout &dl) {
1607 assert(mlir::cast<cir::PointerType>(bytePtr.getType()).getPointee() ==
1609 "the bump strides in bytes, so the pointee must be u8");
1610 assert(llvm::isPowerOf2_64(align) &&
1611 "mask rounding needs a power-of-two alignment");
1612 mlir::Value bumped =
1614 std::optional<uint64_t> indexWidth =
1615 dl.getTypeIndexBitwidth(bytePtr.getType());
1616 assert(indexWidth &&
"a pointer in the argument area has an index width");
1618 static_cast<unsigned>(*indexWidth));
1619 return cir::PtrMaskOp::create(b, loc, bytePtr.getType(), bumped, mask);
1625 const VAArgFetch &f) {
1628 f.loc, b.
getPointerTo(f.vaFields[2]), f.valist,
"overflow_arg_area", 2);
1629 mlir::Value overflow = b.
createLoad(f.loc, overflowP);
1632 uint64_t tyAlign = argumentAreaAlign(f.resultTy, f.module, f.dl);
1634 bytePtr = roundPointerUpToAlignment(b, f.loc, bytePtr, tyAlign, f.dl);
1636 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
1637 uint64_t stride = (tySize + 7) & ~UINT64_C(7);
1638 mlir::Value strideVal = b.
getSignedInt(f.loc, stride, 32);
1649 unsigned neededInt,
unsigned neededSse) {
1650 RegisterCursor cursor;
1653 f.valist,
"gp_offset", 0);
1654 cursor.gpOffset = b.
createLoad(f.loc, cursor.gpOffsetP);
1656 b.
getConstantInt(f.loc, cursor.gpOffset.getType(), 48 - neededInt * 8);
1658 b.
createCompare(f.loc, cir::CmpOpKind::le, cursor.gpOffset, limit);
1662 f.valist,
"fp_offset", 1);
1663 cursor.fpOffset = b.
createLoad(f.loc, cursor.fpOffsetP);
1664 mlir::Value limit = b.
getConstantInt(f.loc, cursor.fpOffset.getType(),
1665 176 - neededSse * 16);
1666 mlir::Value fitsInFp =
1667 b.
createCompare(f.loc, cir::CmpOpKind::le, cursor.fpOffset, limit);
1668 cursor.inRegs = cursor.inRegs
1678 const ArgClassification &ac,
1679 const RegisterCursor &cursor,
1680 const std::array<bool, 2> &pairIsSse,
1681 mlir::Value regSaveArea, mlir::Value regPairTemp) {
1682 auto pairTy = mlir::cast<cir::RecordType>(ac.coercedType);
1686 bool bothSse = pairIsSse[0] && pairIsSse[1];
1689 unsigned seenOfClass[2] = {0, 0};
1690 for (
unsigned i = 0; i < 2; ++i) {
1691 bool isSse = pairIsSse[i];
1692 mlir::Value base = isSse ? cursor.fpOffset : cursor.gpOffset;
1693 unsigned regSize = isSse ? 16 : 8;
1694 unsigned prior = seenOfClass[isSse];
1695 ++seenOfClass[isSse];
1696 mlir::Value off = base;
1702 mlir::Type elemTy = pairTy.getElementType(i);
1713uint64_t registerSlotSize(
unsigned neededInt,
unsigned neededSse) {
1714 assert(!(neededInt && neededSse) &&
1715 "a fetch needing both classes is a pair, reassembled elsewhere");
1716 return neededSse ? neededSse * 16 : neededInt * 8;
1721bool needsTempCopy(
const VAArgFetch &f,
unsigned neededInt,
1722 unsigned neededSse) {
1723 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
1724 if (f.ac.coercedType &&
1725 (f.ac.directOffset || registerSlotSize(neededInt, neededSse) < tySize))
1729 uint64_t slotAlign = neededSse ? 16 : 8;
1730 return argumentAreaAlign(f.resultTy, f.module, f.dl) > slotAlign;
1736 const VAArgFetch &f, mlir::Value regAddr,
1737 mlir::Value temp,
unsigned neededInt,
1738 unsigned neededSse) {
1740 uint64_t tySize = f.dl.getTypeSize(f.resultTy).getFixedValue();
1742 if (f.ac.coercedType &&
1743 (f.ac.directOffset || registerSlotSize(neededInt, neededSse) < tySize)) {
1750 mlir::Value dst = temp;
1751 if (f.ac.directOffset) {
1766 const RegisterCursor &cursor,
unsigned neededInt,
1767 unsigned neededSse) {
1788 const ArgClassification &ac,
1789 mlir::OpBuilder &opBuilder) {
1790 auto op = mlir::cast<cir::VAArgOp>(vaArgOp);
1792 mlir::Location loc = op.getLoc();
1793 mlir::Type resultTy = op.getType();
1794 mlir::Value valist = op.getArgList();
1799 if (ac.kind == ArgKind::Ignore) {
1800 builder.setInsertionPoint(op);
1801 op.getResult().replaceAllUsesWith(
1802 createIgnoredValue(builder, loc, resultTy));
1804 return mlir::success();
1807 if (ac.kind == ArgKind::Indirect && !ac.byVal)
1808 return reportVAArgNYI(op,
"a non-trivially-copyable type");
1813 unsigned neededInt = ac.neededIntRegs;
1814 unsigned neededSse = ac.neededSseRegs;
1818 std::array<bool, 2> pairIsSse = {
false,
false};
1819 bool isRegPair =
false;
1820 if (ac.kind == ArgKind::Direct && neededInt + neededSse == 2 &&
1822 if (classifyRegisterPair(op, ac, pairIsSse, isRegPair).failed())
1823 return mlir::failure();
1826 auto vaListRecTy = mlir::dyn_cast<cir::RecordType>(
1827 mlir::cast<cir::PointerType>(valist.getType()).getPointee());
1828 if (!vaListRecTy || vaListRecTy.getNumElements() != 4) {
1829 return reportVAArgNYI(op,
1830 "a va_list that is not the four-field gp_offset / "
1831 "fp_offset / overflow_arg_area / reg_save_area "
1835 const VAArgFetch fetch{loc, valist, vaListRecTy.getMembers(), resultTy, ac,
1839 builder.setInsertionPoint(op);
1842 if (neededInt == 0 && neededSse == 0) {
1843 addr = buildOverflowAddrAndAdvance(builder, fetch);
1845 RegisterCursor cursor =
1846 buildRegisterGate(builder, fetch, neededInt, neededSse);
1854 bool pairNeedsReassembly = isRegPair && neededSse != 0;
1857 mlir::Value regPairTemp;
1858 if (pairNeedsReassembly) {
1863 loc, builder.
getPointerTo(ac.coercedType),
"vaarg.reg",
1865 std::max(dl.getTypeABIAlignment(ac.coercedType),
1866 argumentAreaAlign(resultTy, module, dl))));
1869 mlir::Value regTemp;
1870 bool copyThroughTemp =
1871 !pairNeedsReassembly && needsTempCopy(fetch, neededInt, neededSse);
1872 if (copyThroughTemp) {
1876 argumentAreaAlign(resultTy, module, dl)));
1879 addr = cir::TernaryOp::create(
1880 builder, loc, cursor.inRegs,
1882 [&](mlir::OpBuilder &ob, mlir::Location l) {
1883 CIRBaseBuilderTy b(ob);
1884 mlir::Value regSaveArea = b.createLoad(
1885 l, b.createGetMember(l, b.getPointerTo(fetch.vaFields[3]),
1886 valist,
"reg_save_area", 3));
1887 regSaveArea = b.createPtrBitcast(regSaveArea, byteTy);
1889 mlir::Value regAddr;
1890 if (pairNeedsReassembly) {
1891 reassembleRegisterPair(b, l, ac, cursor, pairIsSse,
1892 regSaveArea, regPairTemp);
1893 regAddr = b.createPtrBitcast(regPairTemp, byteTy);
1896 neededSse ? cursor.fpOffset : cursor.gpOffset;
1897 regAddr = b.createPtrStride(l, regSaveArea, off);
1898 if (copyThroughTemp) {
1899 regAddr = copyRegisterToTemp(b, l, fetch, regAddr, regTemp,
1900 neededInt, neededSse);
1904 advanceRegisterCursors(b, l, cursor, neededInt, neededSse);
1905 cir::YieldOp::create(b, l, regAddr);
1908 [&](mlir::OpBuilder &ob, mlir::Location l) {
1910 mlir::Value memAddr = buildOverflowAddrAndAdvance(b, fetch);
1911 cir::YieldOp::create(b, l, memAddr);
1917 mlir::Value result =
1918 builder.createAlignedLoad(loc, builder.createPtrBitcast(addr, resultTy),
1919 argumentAreaAlign(resultTy, module, dl));
1920 op.getResult().replaceAllUsesWith(result);
1922 return mlir::success();
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 normalizeParameterSlotAlignments(cir::FuncOp funcOp, const mlir::abi::FunctionClassification &fc)
Restate each non-byval indirect parameter's CIRGen slot alignment as the alignment the ABI promises f...
void finalizeParameterSlots()
Replace each non-byval indirect parameter's CIRGen slot with the incoming pointer,...
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={})
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)
fromQuantity - 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...
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)
static bool deadOnReturnAttr()
static bool noaliasOnByvalAttr()