36#include "mlir/ABI/ABIRewriteContext.h"
37#include "mlir/ABI/ABITypeMapper.h"
38#include "mlir/ABI/Targets/Test/TestTarget.h"
39#include "mlir/Dialect/DLTI/DLTI.h"
40#include "mlir/IR/Builders.h"
41#include "mlir/IR/BuiltinOps.h"
42#include "mlir/IR/SymbolTable.h"
43#include "mlir/Interfaces/DataLayoutInterfaces.h"
44#include "mlir/Pass/Pass.h"
47#include "llvm/ABI/FunctionInfo.h"
48#include "llvm/ABI/TargetInfo.h"
49#include "llvm/ABI/Types.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/IR/CallingConv.h"
54using namespace mlir::abi;
58#define GEN_PASS_DEF_CALLCONVLOWERING
59#include "clang/CIR/Dialect/Passes.h.inc"
85 return layout.getArgPassingKind() == cir::ArgPassingKind::CanPassInRegs;
94static llvm::Align recordDeclaredAlign(ModuleOp modOp,
cir::RecordType recTy,
95 const DataLayout &dl) {
98 return llvm::Align(dl.getTypeABIAlignment(recTy));
99 return llvm::Align(layout.getRecordAlign());
108static bool isSupportedType(mlir::Type ty,
const DataLayout &dl) {
112 if (
auto ptrTy = dyn_cast<cir::PointerType>(ty))
113 return !ptrTy.getAddrSpace() ||
114 mlir::isa<cir::TargetAddressSpaceAttr>(ptrTy.getAddrSpace());
115 if (isa<cir::VoidType, cir::BoolType>(ty))
119 if (isa<cir::FPTypeInterface>(ty))
121 if (
auto intTy = dyn_cast<cir::IntType>(ty)) {
132 if (intTy.getIsBitInt())
133 return intTy.getWidth() <= 128;
134 return intTy.getWidth() <= 64 || intTy.getWidth() == 128;
136 if (
auto complexTy = dyn_cast<cir::ComplexType>(ty))
137 return isSupportedType(complexTy.getElementType(), dl);
138 if (
auto arrTy = dyn_cast<cir::ArrayType>(ty))
139 return isSupportedType(arrTy.getElementType(), dl);
140 if (
auto recTy = dyn_cast<cir::RecordType>(ty)) {
152 uint64_t recordBits = dl.getTypeSizeInBits(recTy).getFixedValue();
153 if (members.empty()) {
157 if (recordBits > 128)
160 auto spansRecord = [&](mlir::Type m) {
161 return dl.getTypeSizeInBits(m).getFixedValue() == recordBits;
163 if (!llvm::any_of(members, spansRecord))
172 [&](mlir::Type m) { return isSupportedType(m, dl); });
178static mlir::Type abiTypeToCIR(
const llvm::abi::Type *ty, MLIRContext *ctx) {
181 return llvm::TypeSwitch<const llvm::abi::Type *, mlir::Type>(ty)
183 [&](
const llvm::abi::VoidType *) {
return cir::VoidType::get(ctx); })
184 .Case([&](
const llvm::abi::IntegerType *intTy) {
185 return cir::IntType::get(ctx, intTy->getSizeInBits().getFixedValue(),
186 intTy->isSigned(), intTy->isBitInt());
188 .Case([&](
const llvm::abi::FloatType *fltTy) {
191 .Case([&](
const llvm::abi::PointerType *) {
192 return cir::PointerType::get(cir::VoidType::get(ctx));
194 .Case([&](
const llvm::abi::VectorType *vecTy) -> mlir::Type {
195 mlir::Type elemCIR = abiTypeToCIR(vecTy->getElementType(), ctx);
198 return cir::VectorType::get(elemCIR,
199 vecTy->getNumElements().getFixedValue());
201 .Case([&](
const llvm::abi::RecordType *recTy) -> mlir::Type {
203 fieldTypes.reserve(recTy->getFields().size());
204 for (
const auto &field : recTy->getFields()) {
205 mlir::Type fieldCIR = abiTypeToCIR(field.FieldType, ctx);
208 fieldTypes.push_back(fieldCIR);
211 return cir::StructType::get(ctx, fieldTypes,
false,
214 .
Default([](
const llvm::abi::Type *) -> mlir::Type {
return nullptr; });
220static const llvm::abi::Type *mapCIRType(mlir::Type type,
221 mlir::abi::ABITypeMapper &typeMapper,
222 const DataLayout &dl, ModuleOp modOp) {
223 llvm::abi::TypeBuilder &tb = typeMapper.getTypeBuilder();
224 return llvm::TypeSwitch<mlir::Type, const llvm::abi::Type *>(type)
225 .Case([&](cir::IntType intTy) {
226 return tb.getIntegerType(intTy.getWidth(),
227 llvm::Align(dl.getTypeABIAlignment(type)),
228 intTy.isSigned(), intTy.getIsBitInt());
230 .Case([&](cir::PointerType ptrTy) {
231 unsigned addrSpace = 0;
232 if (
auto targetAsAttr =
233 dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
234 ptrTy.getAddrSpace()))
235 addrSpace = targetAsAttr.getValue();
236 return tb.getPointerType(dl.getTypeSizeInBits(type),
237 llvm::Align(dl.getTypeABIAlignment(type)),
240 .Case([&](cir::BoolType) {
241 return tb.getIntegerType(dl.getTypeSizeInBits(type),
242 llvm::Align(dl.getTypeABIAlignment(type)),
245 .Case([&](cir::VoidType) {
return tb.getVoidType(); })
246 .Case([&](cir::FPTypeInterface fpTy) {
249 return tb.getFloatType(fpTy.getFloatSemantics(),
250 llvm::Align(dl.getTypeABIAlignment(type)));
252 .Case([&](cir::ComplexType complexTy) {
253 return tb.getComplexType(
254 mapCIRType(complexTy.getElementType(), typeMapper, dl, modOp),
255 llvm::Align(dl.getTypeABIAlignment(type)));
257 .Case([&](cir::ArrayType arrTy) {
258 const llvm::abi::Type *elemAbi =
259 mapCIRType(arrTy.getElementType(), typeMapper, dl, modOp);
260 return tb.getArrayType(elemAbi, arrTy.getSize(),
261 dl.getTypeSizeInBits(type).getFixedValue());
264 llvm::abi::RecordFlags flags = llvm::abi::RecordFlags::None;
265 if (recordCanPassInRegs(modOp, recTy))
266 flags = flags | llvm::abi::RecordFlags::CanPassInRegisters;
267 llvm::TypeSize sizeBits = llvm::TypeSize::getFixed(
268 dl.getTypeSizeInBits(type).getFixedValue());
269 llvm::Align
align = recordDeclaredAlign(modOp, recTy, dl);
278 fields.push_back(llvm::abi::FieldInfo(
279 mapCIRType(fieldTy, typeMapper, dl, modOp)));
280 return tb.getUnionType(fields, sizeBits, align,
281 llvm::abi::StructPacking::Default, flags);
286 uint64_t offsetBits = 0;
287 for (mlir::Type fieldTy : recTy.
getMembers()) {
288 const llvm::abi::Type *mappedField =
289 mapCIRType(fieldTy, typeMapper, dl, modOp);
291 llvm::alignTo(offsetBits, dl.getTypeABIAlignment(fieldTy) * 8);
292 fields.push_back(llvm::abi::FieldInfo(mappedField, offsetBits));
293 offsetBits += dl.getTypeSizeInBits(fieldTy).getFixedValue();
295 return tb.getRecordType(
296 fields, sizeBits, align, llvm::abi::StructPacking::Default,
299 .
Default([](mlir::Type) ->
const llvm::abi::Type * {
301 "mapCIRType: type not pre-filtered by classifyX86_64Function");
327static std::optional<ArgClassification>
328convertABIArgInfo(
const llvm::abi::ArgInfo &info, MLIRContext *ctx,
330 if (
info.isDirect()) {
333 const llvm::abi::Type *coerceAbi =
info.getCoerceToType();
334 bool isAggregate = isa_and_present<cir::RecordType, cir::ArrayType>(origTy);
337 bool comparesAgainstCoerce =
338 coerceAbi && isa_and_present<cir::ComplexType>(origTy);
339 bool coerceIsRegisterTuple =
340 isa_and_present<llvm::abi::RecordType>(coerceAbi);
343 auto origInt = dyn_cast_if_present<cir::IntType>(origTy);
344 const auto *coerceInt =
345 dyn_cast_if_present<llvm::abi::IntegerType>(coerceAbi);
346 bool coerceWidensScalar =
347 origInt && coerceInt &&
348 coerceInt->getSizeInBits().getFixedValue() > origInt.getWidth();
352 if (!isAggregate && !comparesAgainstCoerce && !coerceIsRegisterTuple &&
354 return ArgClassification::getDirect(
nullptr);
355 mlir::Type coerced = abiTypeToCIR(coerceAbi, ctx);
360 if (comparesAgainstCoerce && coerced == origTy)
361 return ArgClassification::getDirect(
nullptr);
362 return ArgClassification::getDirect(coerced);
364 if (
info.isExtend()) {
365 if (isa_and_present<cir::BoolType>(origTy))
366 return ArgClassification::getExtend(
nullptr,
info.isSignExt());
367 assert((!origTy || isa<cir::IntType>(origTy)) &&
368 "the x86_64 classifier only returns Extend for integers and bool");
369 mlir::Type extendedTy = abiTypeToCIR(
info.getCoerceToType(), ctx);
370 return ArgClassification::getExtend(extendedTy,
info.isSignExt());
372 if (
info.isIndirect())
373 return ArgClassification::getIndirect(
info.getIndirectAlign(),
374 info.getIndirectByVal());
375 assert(
info.isIgnore() &&
"Unexpected classification");
376 return ArgClassification::getIgnore();
384static llvm::abi::RequiredArgs requiredArgs(cir::FuncType fnTy) {
385 if (!fnTy.isVarArg())
386 return llvm::abi::RequiredArgs::All;
387 return llvm::abi::RequiredArgs(fnTy.getNumInputs());
398static std::optional<FunctionClassification> classifyX86_64Signature(
399 mlir::Type retCIR, mlir::TypeRange inputs, llvm::abi::RequiredArgs required,
400 MLIRContext *ctx,
const DataLayout &dl,
401 mlir::abi::ABITypeMapper &typeMapper,
402 const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp,
403 llvm::function_ref<mlir::InFlightDiagnostic()> emitError) {
404 assert(retCIR &&
"signature return type must be non-null");
405 assert((!required.allowsOptionalArgs() ||
406 required.getNumRequiredArgs() <= inputs.size()) &&
407 "declared parameters cannot outnumber the classified arguments");
408 bool voidRet = isa<cir::VoidType>(retCIR);
410 auto reject = [&](mlir::Type t) ->
bool {
411 if (isSupportedType(t, dl))
414 <<
"x86_64 calling-convention lowering not yet implemented for type "
418 if (!voidRet && reject(retCIR))
420 for (mlir::Type a : inputs)
424 const llvm::abi::Type *retAbi =
425 voidRet ? typeMapper.getTypeBuilder().getVoidType()
426 : mapCIRType(retCIR, typeMapper, dl, modOp);
428 for (mlir::Type a : inputs)
429 argAbi.push_back(mapCIRType(a, typeMapper, dl, modOp));
431 std::unique_ptr<llvm::abi::FunctionInfo> fi = llvm::abi::FunctionInfo::create(
432 llvm::CallingConv::C, retAbi, argAbi, required);
433 targetInfo.computeInfo(*fi);
437 auto nyiCoercion = [&](mlir::Type t) {
438 emitError() <<
"x86_64 calling-convention lowering not yet "
439 "implemented for the ABI coercion of type "
443 FunctionClassification fc;
444 fc.returnsVoid = voidRet;
445 mlir::Type origRet = voidRet ? mlir::Type() : retCIR;
446 std::optional<ArgClassification> retAc =
447 convertABIArgInfo(fi->getReturnInfo(), ctx, origRet);
452 fc.returnInfo = *retAc;
453 for (
unsigned i = 0, e = fi->arg_size(); i < e; ++i) {
454 mlir::Type origArg = i < inputs.size() ? inputs[i] : mlir::Type();
455 std::optional<ArgClassification> ac =
456 convertABIArgInfo(fi->getArgInfo(i).Info, ctx, origArg);
458 nyiCoercion(origArg);
461 fc.argInfos.push_back(*ac);
469static std::optional<FunctionClassification>
470classifyX86_64Function(cir::FuncOp func,
const DataLayout &dl,
471 mlir::abi::ABITypeMapper &typeMapper,
472 const llvm::abi::TargetInfo &targetInfo,
474 cir::FuncType fnTy = func.getFunctionType();
475 return classifyX86_64Signature(fnTy.getReturnType(), fnTy.getInputs(),
476 requiredArgs(fnTy), func->getContext(), dl,
477 typeMapper, targetInfo, modOp,
478 [&]() { return func.emitOpError(); });
488static std::optional<FunctionClassification> classifyX86_64VariadicCall(
489 cir::CIRCallOpInterface call, cir::FuncType calleeTy,
const DataLayout &dl,
490 mlir::abi::ABITypeMapper &typeMapper,
491 const llvm::abi::TargetInfo &targetInfo, ModuleOp modOp) {
492 assert(calleeTy.isVarArg() &&
493 "only a variadic callee can take more operands than it declares");
494 Operation *op = call.getOperation();
495 return classifyX86_64Signature(
496 calleeTy.getReturnType(), call.getArgOperands().getTypes(),
497 requiredArgs(calleeTy), op->getContext(), dl, typeMapper, targetInfo,
498 modOp, [&]() { return op->emitOpError(); });
508static bool classifiesSamePrefix(
const FunctionClassification &calleeFc,
509 const FunctionClassification &callFc) {
510 if (callFc.argInfos.size() < calleeFc.argInfos.size())
512 return calleeFc.returnInfo == callFc.returnInfo &&
513 std::equal(calleeFc.argInfos.begin(), calleeFc.argInfos.end(),
514 callFc.argInfos.begin());
518struct CallConvLoweringPass
519 :
public impl::CallConvLoweringBase<CallConvLoweringPass> {
520 using CallConvLoweringBase::CallConvLoweringBase;
522 CallConvLoweringPass(
const CallConvLoweringOptions &options,
523 const llvm::abi::ABICompatInfo &x86AbiCompat)
524 : CallConvLoweringBase(options), x86AbiCompat(x86AbiCompat) {}
526 void runOnOperation()
override;
532 llvm::abi::ABICompatInfo x86AbiCompat;
541static std::optional<FunctionClassification>
542withReturnVoidness(std::optional<FunctionClassification> fc,
543 mlir::Type returnType) {
545 fc->returnsVoid = mlir::isa<cir::VoidType>(returnType);
553std::optional<FunctionClassification>
554classifyFunction(cir::FuncOp func,
const DataLayout &dl,
557 Type returnType = func.getFunctionType().getReturnType();
559 if (!classificationAttrName.empty()) {
560 auto attr = func->getAttrOfType<DictionaryAttr>(classificationAttrName);
563 <<
"missing classification attribute '" << classificationAttrName
564 <<
"' (CallConvLowering driver mode 'classification-attr')";
567 return withReturnVoidness(mlir::abi::test::parseClassificationAttr(
568 attr, [&]() {
return func.emitOpError(); }),
575 "classifyFunction only handles the test target");
576 return withReturnVoidness(mlir::abi::test::classify(argTypes, returnType, dl),
585cir::FuncOp lookupCallee(Operation *callOp, SymbolTable &symbolTable) {
586 FlatSymbolRefAttr callee;
587 if (
auto call = dyn_cast<cir::CallOp>(callOp))
588 callee = call.getCalleeAttr();
589 else if (
auto tryCall = dyn_cast<cir::TryCallOp>(callOp))
590 callee = tryCall.getCalleeAttr();
595 return symbolTable.lookup<cir::FuncOp>(callee.getValue());
603cir::FuncType indirectCalleeType(cir::CIRCallOpInterface call) {
604 if (!call.isIndirect())
606 return cast<cir::FuncType>(
607 cast<cir::PointerType>(call.getIndirectCall().getType()).getPointee());
610void CallConvLoweringPass::runOnOperation() {
611 ModuleOp moduleOp = getOperation();
612 MLIRContext *ctx = &getContext();
615 bool haveAttr = !classificationAttr.empty();
616 if (haveTarget == haveAttr) {
617 moduleOp.emitOpError() <<
"CallConvLowering requires exactly one of "
618 "'target' or 'classification-attr' pass options";
623 if (!moduleOp->hasAttr(DLTIDialect::kDataLayoutAttrName)) {
624 moduleOp.emitOpError()
625 <<
"CallConvLowering requires a DataLayout (dlti.dl_spec attribute "
631 DataLayout dl(moduleOp);
632 CIRABIRewriteContext rewriteCtx(moduleOp, dl);
633 SymbolTable symbolTable(moduleOp);
637 std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
638 std::unique_ptr<llvm::abi::TargetInfo> x86Target;
640 x86TypeMapper.emplace(dl);
641 x86Target = llvm::abi::createX86_64TargetInfo(
642 x86TypeMapper->getTypeBuilder(), x86AvxAbiLevel.getValue(),
649 llvm::MapVector<cir::FuncOp, FunctionClassification> classifications;
650 bool anyFailed =
false;
651 moduleOp.walk([&](cir::FuncOp f) {
652 std::optional<FunctionClassification> fc;
654 fc = classifyX86_64Function(f, dl, *x86TypeMapper, *x86Target, moduleOp);
656 fc = classifyFunction(f, dl, target, classificationAttr);
661 classifications.insert({f, std::move(*fc)});
677 llvm::DenseMap<cir::FuncOp, SmallVector<Operation *>> callers;
681 llvm::DenseMap<Operation *, FunctionClassification> variadicCallSites;
682 moduleOp.walk([&](Operation *op) {
683 auto call = dyn_cast<cir::CIRCallOpInterface>(op);
686 cir::FuncOp callee = lookupCallee(op, symbolTable);
689 callers[callee].push_back(op);
694 cir::FuncType calleeTy = callee.getFunctionType();
695 if (!x86Target || call.getNumArgOperands() <= calleeTy.getNumInputs())
701 if (!calleeTy.isVarArg()) {
702 op->emitOpError() <<
"extra arguments to a callee without a prototype "
703 "not yet implemented in CallConvLowering";
707 std::optional<FunctionClassification> fc = classifyX86_64VariadicCall(
708 call, calleeTy, dl, *x86TypeMapper, *x86Target, moduleOp);
713 variadicCallSites.insert({op, std::move(*fc)});
726 llvm::DenseMap<cir::FuncOp, SmallVector<cir::GetGlobalOp>> addressTakers;
727 moduleOp.walk([&](cir::GetGlobalOp getGlobal) {
728 auto ptrTy = cast<cir::PointerType>(getGlobal.getAddr().getType());
729 if (!isa<cir::FuncType>(ptrTy.getPointee()))
734 auto callee = cast<cir::FuncOp>(symbolTable.lookup(getGlobal.getName()));
735 addressTakers[callee].push_back(getGlobal);
749 OpBuilder builder(ctx);
750 for (
auto &kv : classifications) {
751 cir::FuncOp func = kv.first;
752 const FunctionClassification &fc = kv.second;
753 if (failed(rewriteCtx.rewriteFunctionDefinition(func, fc, builder))) {
757 for (Operation *callOp : callers.lookup(func)) {
758 const FunctionClassification *callFc = &fc;
759 if (
auto it = variadicCallSites.find(callOp);
760 it != variadicCallSites.end()) {
761 callFc = &it->second;
762 assert(classifiesSamePrefix(fc, *callFc) &&
763 "a call site's declared parameters must be classified the same "
764 "way as the callee's");
766 if (failed(rewriteCtx.rewriteCallSite(callOp, *callFc, builder))) {
771 for (cir::GetGlobalOp addrOp : addressTakers.lookup(func))
772 rewriteCtx.rewriteFunctionAddress(addrOp, func, builder);
781 SmallVector<cir::CIRCallOpInterface> indirectCalls;
782 moduleOp.walk([&](cir::CIRCallOpInterface c) {
783 cir::FuncType calleeTy = indirectCalleeType(c);
791 if (!calleeTy.isVarArg() && isa<cir::TryCallOp>(c.getOperation()))
793 indirectCalls.push_back(c);
795 for (cir::CIRCallOpInterface c : indirectCalls) {
799 if (!classificationAttr.empty()) {
800 c->emitOpError() <<
"indirect call cannot be classified in the "
801 "'classification-attr' driver mode";
805 cir::FuncType funcTy = indirectCalleeType(c);
806 auto classifySignature =
807 [&](mlir::TypeRange argTypes) -> std::optional<FunctionClassification> {
809 return classifyX86_64Signature(funcTy.getReturnType(), argTypes,
810 requiredArgs(funcTy), ctx, dl,
811 *x86TypeMapper, *x86Target, moduleOp,
812 [&]() {
return c->emitOpError(); });
813 return withReturnVoidness(
814 mlir::abi::test::classify(argTypes, funcTy.getReturnType(), dl),
815 funcTy.getReturnType());
824 if (c.getNumArgOperands() > funcTy.getNumInputs()) {
825 std::optional<FunctionClassification> callFc =
826 classifySignature(c.getArgOperands().getTypes());
831 if (!callFc->needsRewrite())
833 c->emitOpError() <<
"variadic arguments to an indirect call not yet "
834 "implemented in CallConvLowering";
839 std::optional<FunctionClassification> fc =
840 classifySignature(funcTy.getInputs());
845 if (failed(rewriteCtx.rewriteCallSite(c.getOperation(), *fc, builder))) {
855 return std::make_unique<CallConvLoweringPass>();
860 llvm::abi::X86AVXABILevel x86AvxAbiLevel,
861 const llvm::abi::ABICompatInfo &x86AbiCompat) {
862 CallConvLoweringOptions options;
863 options.target = target;
864 options.x86AvxAbiLevel = x86AvxAbiLevel;
865 return std::make_unique<CallConvLoweringPass>(options, x86AbiCompat);
C++ view class that accepts both !cir.struct and !cir.union types.
llvm::ArrayRef< mlir::Type > getMembers() const
mlir::StringAttr getName() const
RecordLayoutAttr tryGetRecordLayout(mlir::ModuleOp mod, mlir::StringAttr name)
Same lookup as getRecordLayout, but returns a null attribute instead of asserting when the record has...
cir::FPTypeInterface getFloatingPointType(const llvm::fltSemantics &sem, mlir::MLIRContext *ctx)
Returns the CIR floating-point type for the given semantics, or a null type if CIR has no type for it...
CallConvTarget
The ABI target whose calling-convention rules drive CallConvLowering.
const internal::VariadicAllOfMatcher< Attr > attr
constexpr size_t align(size_t Size)
Aligns a size to the pointer alignment.
void info(bool Verbose, unsigned Level, const char *Fmt, Ts &&...Args)
Prints an indented note to stderr when Verbose is set.
@ Default
Set to the current date and time.
std::unique_ptr< Pass > createCallConvLoweringPass()