================
@@ -55,6 +61,219 @@ namespace mlir {
namespace {
+//===----------------------------------------------------------------------===//
+// x86_64 System V classifier bridge (scalar types)
+//
+// Maps scalar CIR types to llvm::abi::Type, runs the LLVM ABI Lowering
+// Library's SysV x86_64 classifier, and converts the result back into the
+// dialect-agnostic mlir::abi::FunctionClassification that CIRABIRewriteContext
+// consumes. Only integer / pointer / bool / f32 / f64 signatures are handled;
+// aggregates and other leaf types are reported NYI by classifyX86_64Function
+// so an unsupported signature fails the pass instead of being misclassified.
+//===----------------------------------------------------------------------===//
+
+/// llvm::Align requires a power of two; DataLayout can report non-power-of-two
+/// alignments for unusual types.
+static llvm::Align safeAlign(uint64_t a) {
+ return llvm::Align(llvm::PowerOf2Ceil(std::max<uint64_t>(a, 1)));
+}
+
+/// The scalar CIR types the x86_64 bridge handles. A regular integer up to
+/// 64 bits, pointer, bool, void, f32, or f64 is a single-register Direct or
+/// Extend argument. `_BitInt`, `__int128`, and wider/other types need
coercion
+/// or indirect passing, which this scalar bridge does not do.
+static bool isSupportedScalarType(mlir::Type ty) {
+ if (isa<cir::VoidType, cir::BoolType, cir::PointerType, cir::SingleType,
+ cir::DoubleType>(ty))
+ return true;
+ if (auto intTy = dyn_cast<cir::IntType>(ty))
+ return !intTy.getIsBitInt() && intTy.getWidth() <= 64;
+ return false;
+}
+
+/// Convert an llvm::abi::Type coercion type back to a scalar CIR type.
+static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, MLIRContext *ctx) {
+ if (!ty)
+ return nullptr;
+ return llvm::TypeSwitch<const llvm::abi::Type *, mlir::Type>(ty)
+ .Case(
+ [&](const llvm::abi::VoidType *) { return cir::VoidType::get(ctx); })
+ .Case([&](const llvm::abi::IntegerType *intTy) {
+ return cir::IntType::get(ctx, intTy->getSizeInBits().getFixedValue(),
+ intTy->isSigned());
+ })
+ .Case([&](const llvm::abi::FloatType *fltTy) {
+ return cir::getFloatingPointType(*fltTy->getSemantics(), ctx);
+ })
+ .Case([&](const llvm::abi::PointerType *) {
+ return cir::PointerType::get(cir::VoidType::get(ctx));
+ })
+ .Default([](const llvm::abi::Type *) -> mlir::Type { return nullptr; });
+}
+
+/// Map a scalar CIR type to an llvm::abi::Type. classifyX86_64Function
+/// pre-filters the signature, so only the scalar types handled here can
+/// reach this function.
+static const llvm::abi::Type *mapCIRType(mlir::Type type,
+ mlir::abi::ABITypeMapper &typeMapper,
+ const DataLayout &dl) {
+ llvm::abi::TypeBuilder &tb = typeMapper.getTypeBuilder();
+ return llvm::TypeSwitch<mlir::Type, const llvm::abi::Type *>(type)
+ .Case([&](cir::IntType intTy) {
+ return tb.getIntegerType(intTy.getWidth(),
+ safeAlign(dl.getTypeABIAlignment(type)),
+ intTy.isSigned());
+ })
+ .Case([&](cir::PointerType ptrTy) {
+ unsigned addrSpace = 0;
+ if (auto targetAsAttr =
+ dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
+ ptrTy.getAddrSpace()))
+ addrSpace = targetAsAttr.getValue();
+ return tb.getPointerType(dl.getTypeSizeInBits(type),
+ safeAlign(dl.getTypeABIAlignment(type)),
+ addrSpace);
+ })
+ .Case([&](cir::BoolType) {
+ return tb.getIntegerType(dl.getTypeSizeInBits(type),
+ safeAlign(dl.getTypeABIAlignment(type)),
+ /*Signed=*/false);
+ })
+ .Case([&](cir::VoidType) { return tb.getVoidType(); })
+ .Case([&](cir::SingleType) {
+ return tb.getFloatType(llvm::APFloat::IEEEsingle(),
+ safeAlign(dl.getTypeABIAlignment(type)));
+ })
+ .Case([&](cir::DoubleType) {
+ return tb.getFloatType(llvm::APFloat::IEEEdouble(),
+ safeAlign(dl.getTypeABIAlignment(type)));
+ })
+ .Default([](mlir::Type) -> const llvm::abi::Type * {
+ llvm_unreachable(
+ "mapCIRType: type not pre-filtered by classifyX86_64Function");
+ });
+}
+
+/// Convert an llvm::abi::ArgInfo for a scalar type into the ArgClassification
+/// consumed by CIRABIRewriteContext.
+///
+/// Direct: every scalar this bridge maps passes as-is, so no coercion type
+/// is needed (nullptr means "same as the original CIR type").
+///
+/// Extend: bool or a sub-register integer needs a signext/zeroext attribute.
+/// Every ArgInfo::getExtend() call site in the x86_64 classifier
+/// (llvm/lib/ABI/Targets/X86.cpp) is gated on the operand being an integer,
+/// so a non-integer, non-bool origTy here would mean the classifier
+/// disagreed with its own source -- asserted rather than silently handled.
+///
+/// Indirect: needed once records/_BitInt/vectors are supported (sret,
+/// byval, and large _BitInt all classify Indirect), but
+/// X86_64TargetInfo::getIndirectResult()/getIndirectReturnResult() only
+/// return Indirect for aggregates or _BitInt, neither of which the
+/// scalar-only type set this bridge accepts can produce. Unreachable until
+/// a later PR adds those types and the record/vector/complex/int type gate
+/// that belongs here.
+///
+/// Ignore: a void return has no register or stack slot.
+static ArgClassification convertABIArgInfo(const llvm::abi::ArgInfo &info,
+ MLIRContext *ctx,
+ mlir::Type origTy) {
+ if (info.isDirect())
+ return ArgClassification::getDirect(nullptr);
+ if (info.isExtend()) {
+ if (origTy && isa<cir::BoolType>(origTy))
+ return ArgClassification::getExtend(nullptr, info.isSignExt());
+ assert((!origTy || isa<cir::IntType>(origTy)) &&
+ "the x86_64 classifier only returns Extend for integers and bool");
+ mlir::Type extendedTy = abiTypeToCIR(info.getCoerceToType(), ctx);
+ return ArgClassification::getExtend(extendedTy, info.isSignExt());
+ }
+ if (info.isIndirect())
+ llvm_unreachable("Indirect classification is impossible for the "
+ "scalar-only type set this bridge accepts");
+ return ArgClassification::getIgnore();
+}
+
+/// Classify a cir.func for x86_64 SysV using the LLVM ABI library. Returns
+/// std::nullopt and emits an NYI error if the signature uses a type the scalar
+/// bridge does not handle yet.
+static std::optional<FunctionClassification>
+classifyX86_64Function(cir::FuncOp func, const DataLayout &dl,
+ mlir::abi::ABITypeMapper &typeMapper,
+ const llvm::abi::TargetInfo &targetInfo) {
+ MLIRContext *ctx = func->getContext();
+ cir::FuncType fnTy = func.getFunctionType();
+ mlir::Type retCIR = fnTy.getReturnType();
+ assert(retCIR && "FuncType::getReturnType() never returns null");
+ bool voidRet = isa<cir::VoidType>(retCIR);
+
+ auto reject = [&](mlir::Type t) -> bool {
+ if (isSupportedScalarType(t))
+ return false;
+ func.emitOpError()
+ << "x86_64 calling-convention lowering not yet implemented for type "
+ << t;
+ return true;
+ };
+ if (!voidRet && reject(retCIR))
+ return std::nullopt;
+ for (mlir::Type a : fnTy.getInputs())
+ if (reject(a))
+ return std::nullopt;
+
+ const llvm::abi::Type *retAbi =
+ voidRet ? typeMapper.getTypeBuilder().getVoidType()
+ : mapCIRType(retCIR, typeMapper, dl);
+ SmallVector<const llvm::abi::Type *> argAbi;
+ for (mlir::Type a : fnTy.getInputs())
+ argAbi.push_back(mapCIRType(a, typeMapper, dl));
+
+ std::unique_ptr<llvm::abi::FunctionInfo> fi =
+ llvm::abi::FunctionInfo::create(llvm::CallingConv::C, retAbi, argAbi);
+ targetInfo.computeInfo(*fi);
+
+ FunctionClassification fc;
+ mlir::Type origRet = voidRet ? mlir::Type() : retCIR;
+ fc.returnInfo = convertABIArgInfo(fi->getReturnInfo(), ctx, origRet);
+ auto inputs = fnTy.getInputs();
+ for (unsigned i = 0, e = fi->arg_size(); i < e; ++i) {
+ mlir::Type origArg = i < inputs.size() ? inputs[i] : mlir::Type();
+ fc.argInfos.push_back(
+ convertABIArgInfo(fi->getArgInfo(i).Info, ctx, origArg));
+ }
+ return fc;
+}
+
+/// The classifier targets this pass drives internally via a shared
+/// ABITypeMapper/TargetInfo. (The classification-attr injection mode is
+/// orthogonal -- it names an arbitrary attribute, not a fixed target -- and
+/// stays string-keyed.) kCallConvTargetNames is the single source of truth
+/// for both parsing the `target` pass option and the unknown-target
+/// diagnostic in classifyFunction; it is indexed by the enum value below.
+enum class CallConvTarget { Test, X86_64, Last = X86_64 };
+
+constexpr llvm::StringRef kCallConvTargetNames[] = {"test", "x86_64"};
+static_assert(std::size(kCallConvTargetNames) ==
+ static_cast<size_t>(CallConvTarget::Last) + 1,
+ "kCallConvTargetNames must have one entry per CallConvTarget");
+
+std::optional<CallConvTarget> parseCallConvTarget(StringRef target) {
+ for (size_t i = 0; i < std::size(kCallConvTargetNames); ++i)
+ if (target == kCallConvTargetNames[i])
----------------
adams381 wrote:
Made `target` an enum option with `clEnumValN`. No more name table or
hand-parsing.
https://github.com/llvm/llvm-project/pull/209636
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits