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 auto dict = modOp->getAttrOfType<DictionaryAttr>(
86 cir::CIRDialect::getRecordLayoutsAttrName());
89 auto layout = dict.getAs<cir::RecordLayoutAttr>(
name);
92 return layout.getArgPassingKind() == cir::ArgPassingKind::CanPassInRegs;
101static bool isSupportedType(mlir::Type ty) {
105 if (
auto ptrTy = dyn_cast<cir::PointerType>(ty))
106 return !ptrTy.getAddrSpace() ||
107 mlir::isa<cir::TargetAddressSpaceAttr>(ptrTy.getAddrSpace());
108 if (isa<cir::VoidType, cir::BoolType, cir::SingleType, cir::DoubleType>(ty))
110 if (
auto intTy = dyn_cast<cir::IntType>(ty))
111 return !intTy.getIsBitInt() && intTy.getWidth() <= 64;
112 if (
auto arrTy = dyn_cast<cir::ArrayType>(ty))
113 return isSupportedType(arrTy.getElementType());
114 if (
auto recTy = dyn_cast<cir::RecordType>(ty)) {
127 [](mlir::Type m) { return isSupportedType(m); });
133static mlir::Type abiTypeToCIR(
const llvm::abi::Type *ty, MLIRContext *ctx) {
136 return llvm::TypeSwitch<const llvm::abi::Type *, mlir::Type>(ty)
138 [&](
const llvm::abi::VoidType *) {
return cir::VoidType::get(ctx); })
139 .Case([&](
const llvm::abi::IntegerType *intTy) {
140 return cir::IntType::get(ctx, intTy->getSizeInBits().getFixedValue(),
143 .Case([&](
const llvm::abi::FloatType *fltTy) {
146 .Case([&](
const llvm::abi::PointerType *) {
147 return cir::PointerType::get(cir::VoidType::get(ctx));
149 .Case([&](
const llvm::abi::RecordType *recTy) -> mlir::Type {
151 fieldTypes.reserve(recTy->getFields().size());
152 for (
const auto &field : recTy->getFields()) {
153 mlir::Type fieldCIR = abiTypeToCIR(field.FieldType, ctx);
156 fieldTypes.push_back(fieldCIR);
159 return cir::StructType::get(ctx, fieldTypes,
false,
162 .
Default([](
const llvm::abi::Type *) -> mlir::Type {
return nullptr; });
168static const llvm::abi::Type *mapCIRType(mlir::Type type,
169 mlir::abi::ABITypeMapper &typeMapper,
170 const DataLayout &dl, ModuleOp modOp) {
171 llvm::abi::TypeBuilder &tb = typeMapper.getTypeBuilder();
172 return llvm::TypeSwitch<mlir::Type, const llvm::abi::Type *>(type)
173 .Case([&](cir::IntType intTy) {
174 return tb.getIntegerType(intTy.getWidth(),
175 llvm::Align(dl.getTypeABIAlignment(type)),
178 .Case([&](cir::PointerType ptrTy) {
179 unsigned addrSpace = 0;
180 if (
auto targetAsAttr =
181 dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
182 ptrTy.getAddrSpace()))
183 addrSpace = targetAsAttr.getValue();
184 return tb.getPointerType(dl.getTypeSizeInBits(type),
185 llvm::Align(dl.getTypeABIAlignment(type)),
188 .Case([&](cir::BoolType) {
189 return tb.getIntegerType(dl.getTypeSizeInBits(type),
190 llvm::Align(dl.getTypeABIAlignment(type)),
193 .Case([&](cir::VoidType) {
return tb.getVoidType(); })
194 .Case([&](cir::SingleType) {
195 return tb.getFloatType(llvm::APFloat::IEEEsingle(),
196 llvm::Align(dl.getTypeABIAlignment(type)));
198 .Case([&](cir::DoubleType) {
199 return tb.getFloatType(llvm::APFloat::IEEEdouble(),
200 llvm::Align(dl.getTypeABIAlignment(type)));
202 .Case([&](cir::ArrayType arrTy) {
203 const llvm::abi::Type *elemAbi =
204 mapCIRType(arrTy.getElementType(), typeMapper, dl, modOp);
205 return tb.getArrayType(elemAbi, arrTy.getSize(),
206 dl.getTypeSizeInBits(type).getFixedValue());
214 uint64_t offsetBits = 0;
215 for (mlir::Type fieldTy : recTy.
getMembers()) {
216 const llvm::abi::Type *mappedField =
217 mapCIRType(fieldTy, typeMapper, dl, modOp);
219 llvm::alignTo(offsetBits, dl.getTypeABIAlignment(fieldTy) * 8);
220 fields.push_back(llvm::abi::FieldInfo(mappedField, offsetBits));
221 offsetBits += dl.getTypeSizeInBits(fieldTy).getFixedValue();
223 llvm::abi::RecordFlags flags = llvm::abi::RecordFlags::None;
224 if (recordCanPassInRegs(modOp, recTy))
225 flags = flags | llvm::abi::RecordFlags::CanPassInRegisters;
226 return tb.getRecordType(fields,
227 llvm::TypeSize::getFixed(
228 dl.getTypeSizeInBits(type).getFixedValue()),
229 llvm::Align(dl.getTypeABIAlignment(type)),
230 llvm::abi::StructPacking::Default,
234 .
Default([](mlir::Type) ->
const llvm::abi::Type * {
236 "mapCIRType: type not pre-filtered by classifyX86_64Function");
262static std::optional<ArgClassification>
263convertABIArgInfo(
const llvm::abi::ArgInfo &info, MLIRContext *ctx,
265 if (info.isDirect()) {
267 if (!origTy || !isa<cir::RecordType, cir::ArrayType>(origTy))
268 return ArgClassification::getDirect(
nullptr);
273 mlir::Type coerced = abiTypeToCIR(info.getCoerceToType(), ctx);
276 return ArgClassification::getDirect(coerced);
278 if (info.isExtend()) {
279 if (origTy && isa<cir::BoolType>(origTy))
280 return ArgClassification::getExtend(
nullptr, info.isSignExt());
281 assert((!origTy || isa<cir::IntType>(origTy)) &&
282 "the x86_64 classifier only returns Extend for integers and bool");
283 mlir::Type extendedTy = abiTypeToCIR(info.getCoerceToType(), ctx);
284 return ArgClassification::getExtend(extendedTy, info.isSignExt());
286 if (info.isIndirect())
287 return ArgClassification::getIndirect(info.getIndirectAlign(),
288 info.getIndirectByVal());
289 assert(info.isIgnore() &&
"Unexpected classification");
290 return ArgClassification::getIgnore();
296static std::optional<FunctionClassification>
297classifyX86_64Function(cir::FuncOp func,
const DataLayout &dl,
298 mlir::abi::ABITypeMapper &typeMapper,
299 const llvm::abi::TargetInfo &targetInfo,
301 MLIRContext *ctx = func->getContext();
302 cir::FuncType fnTy = func.getFunctionType();
303 mlir::Type retCIR = fnTy.getReturnType();
304 assert(retCIR &&
"FuncType::getReturnType() never returns null");
305 bool voidRet = isa<cir::VoidType>(retCIR);
307 auto reject = [&](mlir::Type t) ->
bool {
308 if (isSupportedType(t))
311 <<
"x86_64 calling-convention lowering not yet implemented for type "
315 if (!voidRet && reject(retCIR))
317 for (mlir::Type a : fnTy.getInputs())
321 const llvm::abi::Type *retAbi =
322 voidRet ? typeMapper.getTypeBuilder().getVoidType()
323 : mapCIRType(retCIR, typeMapper, dl, modOp);
325 for (mlir::Type a : fnTy.getInputs())
326 argAbi.push_back(mapCIRType(a, typeMapper, dl, modOp));
328 std::unique_ptr<llvm::abi::FunctionInfo> fi =
329 llvm::abi::FunctionInfo::create(llvm::CallingConv::C, retAbi, argAbi);
330 targetInfo.computeInfo(*fi);
335 auto nyiCoercion = [&](mlir::Type t) {
336 func.emitOpError() <<
"x86_64 calling-convention lowering not yet "
337 "implemented for the ABI coercion of type "
341 FunctionClassification fc;
342 mlir::Type origRet = voidRet ? mlir::Type() : retCIR;
343 std::optional<ArgClassification> retAc =
344 convertABIArgInfo(fi->getReturnInfo(), ctx, origRet);
349 fc.returnInfo = *retAc;
350 auto inputs = fnTy.getInputs();
351 for (
unsigned i = 0, e = fi->arg_size(); i < e; ++i) {
352 mlir::Type origArg = i < inputs.size() ? inputs[i] : mlir::Type();
353 std::optional<ArgClassification> ac =
354 convertABIArgInfo(fi->getArgInfo(i).Info, ctx, origArg);
356 nyiCoercion(origArg);
359 fc.argInfos.push_back(*ac);
364bool needsRewrite(
const FunctionClassification &fc) {
365 if ((fc.returnInfo.kind != ArgKind::Direct) || fc.returnInfo.coercedType)
367 for (
const ArgClassification &ac : fc.argInfos)
368 if ((ac.kind != ArgKind::Direct) || ac.coercedType)
373struct CallConvLoweringPass
374 :
public impl::CallConvLoweringBase<CallConvLoweringPass> {
375 using CallConvLoweringBase::CallConvLoweringBase;
376 void runOnOperation()
override;
383std::optional<FunctionClassification>
384classifyFunction(cir::FuncOp func,
const DataLayout &dl,
387 Type returnType = func.getFunctionType().getReturnType();
389 if (!classificationAttrName.empty()) {
390 auto attr = func->getAttrOfType<DictionaryAttr>(classificationAttrName);
393 <<
"missing classification attribute '" << classificationAttrName
394 <<
"' (CallConvLowering driver mode 'classification-attr')";
397 return mlir::abi::test::parseClassificationAttr(
398 attr, [&]() {
return func.emitOpError(); });
404 "classifyFunction only handles the test target");
405 return mlir::abi::test::classify(argTypes, returnType, dl);
413cir::FuncOp lookupCallee(Operation *callOp, SymbolTable &symbolTable) {
414 FlatSymbolRefAttr callee;
415 if (
auto call = dyn_cast<cir::CallOp>(callOp))
416 callee = call.getCalleeAttr();
417 else if (
auto tryCall = dyn_cast<cir::TryCallOp>(callOp))
418 callee = tryCall.getCalleeAttr();
423 return symbolTable.lookup<cir::FuncOp>(callee.getValue());
426void CallConvLoweringPass::runOnOperation() {
427 ModuleOp moduleOp = getOperation();
428 MLIRContext *ctx = &getContext();
431 bool haveAttr = !classificationAttr.empty();
432 if (haveTarget == haveAttr) {
433 moduleOp.emitOpError() <<
"CallConvLowering requires exactly one of "
434 "'target' or 'classification-attr' pass options";
439 if (!moduleOp->hasAttr(DLTIDialect::kDataLayoutAttrName)) {
440 moduleOp.emitOpError()
441 <<
"CallConvLowering requires a DataLayout (dlti.dl_spec attribute "
447 DataLayout dl(moduleOp);
448 CIRABIRewriteContext rewriteCtx(moduleOp, dl);
449 SymbolTable symbolTable(moduleOp);
453 std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
454 std::unique_ptr<llvm::abi::TargetInfo> x86Target;
456 x86TypeMapper.emplace(dl);
457 x86Target = llvm::abi::createX86_64TargetInfo(
458 x86TypeMapper->getTypeBuilder(), x86AvxAbiLevel.getValue(),
459 true, llvm::abi::ABICompatInfo());
465 llvm::MapVector<cir::FuncOp, FunctionClassification> classifications;
466 bool anyFailed =
false;
467 moduleOp.walk([&](cir::FuncOp f) {
468 std::optional<FunctionClassification> fc;
470 fc = classifyX86_64Function(f, dl, *x86TypeMapper, *x86Target, moduleOp);
472 fc = classifyFunction(f, dl, target, classificationAttr);
477 classifications.insert({f, std::move(*fc)});
488 llvm::DenseMap<cir::FuncOp, SmallVector<Operation *>> callers;
489 moduleOp.walk([&](Operation *op) {
490 if (!isa<cir::CallOp, cir::TryCallOp>(op))
492 if (cir::FuncOp callee = lookupCallee(op, symbolTable))
493 callers[callee].push_back(op);
507 OpBuilder builder(ctx);
508 for (
auto &kv : classifications) {
509 cir::FuncOp func = kv.first;
510 const FunctionClassification &fc = kv.second;
511 if (failed(rewriteCtx.rewriteFunctionDefinition(func, fc, builder))) {
515 for (Operation *callOp : callers.lookup(func)) {
516 if (failed(rewriteCtx.rewriteCallSite(callOp, fc, builder))) {
526 const FunctionClassification *rewriteFc =
nullptr;
527 for (
auto &kv : classifications) {
528 if (needsRewrite(kv.second)) {
529 rewriteFc = &kv.second;
534 moduleOp.walk([&](cir::CallOp c) {
537 if (failed(rewriteCtx.rewriteCallSite(c, *rewriteFc, builder)))
550 return std::make_unique<CallConvLoweringPass>();
555 llvm::abi::X86AVXABILevel x86AvxAbiLevel) {
556 CallConvLoweringOptions options;
557 options.target = target;
558 options.x86AvxAbiLevel = x86AvxAbiLevel;
559 return std::make_unique<CallConvLoweringPass>(options);
C++ view class that accepts both !cir.struct and !cir.union types.
llvm::ArrayRef< mlir::Type > getMembers() const
mlir::StringAttr getName() const
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
@ Default
Set to the current date and time.
std::unique_ptr< Pass > createCallConvLoweringPass()