clang 24.0.0git
CallConvLoweringPass.cpp
Go to the documentation of this file.
1//===- CallConvLoweringPass.cpp - Lower CIR to ABI calling convention ----===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass walks every cir.func and cir.call in the module, computes a
10// FunctionClassification for it (via either an ABI target or a pre-built
11// classification injected as a function attribute), and dispatches to
12// CIRABIRewriteContext to perform the actual IR rewriting.
13//
14// Two driver modes (mutually exclusive):
15//
16// target=test
17// Use the MLIR test ABI target (mlir/lib/ABI/Targets/Test/) to classify
18// each function. Predictable rules that approximate x86_64 SysV. Real
19// targets (x86_64, AArch64) will be added once the LLVM ABI library
20// ships them.
21//
22// classification-attr=<name>
23// Read a DictionaryAttr named <name> from each cir.func and parse it via
24// mlir::abi::test::parseClassificationAttr. Used by tests to inject any
25// classification (including shapes the test target itself does not
26// produce) without depending on a real ABI target.
27//
28// The pass requires a `dlti.dl_spec` attribute on the module so the
29// classifier can query type sizes and alignments.
30//
31//===----------------------------------------------------------------------===//
32
33#include "PassDetail.h"
35
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"
52
53using namespace mlir;
54using namespace mlir::abi;
55using namespace cir;
56
57namespace mlir {
58#define GEN_PASS_DEF_CALLCONVLOWERING
59#include "clang/CIR/Dialect/Passes.h.inc"
60} // namespace mlir
61
62namespace {
63
64//===----------------------------------------------------------------------===//
65// x86_64 System V classifier bridge
66//
67// Maps CIR types to llvm::abi::Type, runs the LLVM ABI Lowering Library's
68// SysV x86_64 classifier, and converts the result back into the
69// dialect-agnostic mlir::abi::FunctionClassification that CIRABIRewriteContext
70// consumes. Integer (including `_BitInt` up to 128 bits) / pointer / bool /
71// floating-point scalars are handled, as are struct / union / array aggregates
72// and `_Complex`. Vectors, packed or padded records, and a union no member of
73// which spans its declared size are reported NYI by classifyX86_64Function so
74// an unsupported signature fails the pass instead of being misclassified.
75//===----------------------------------------------------------------------===//
76
77/// Whether a struct's declared argument-passing kind (from the module's
78/// record-layout metadata) allows it to be passed in registers. A record with
79/// no layout entry (e.g. an anonymous struct) has no C++ non-trivial reason to
80/// be forced to memory, so it defaults to can-pass-in-registers.
81static bool recordCanPassInRegs(ModuleOp modOp, cir::RecordType recTy) {
82 auto layout = cir::tryGetRecordLayout(modOp, recTy.getName());
83 if (!layout)
84 return true;
85 return layout.getArgPassingKind() == cir::ArgPassingKind::CanPassInRegs;
86}
87
88/// A record's declared alignment, which the ABI uses for the byval and sret
89/// alignment of an indirect argument. DataLayout derives alignment from the
90/// members, so it cannot see `__attribute__((aligned(N)))`. The declared value
91/// comes from the module's record-layout metadata instead. CIRGen emits an
92/// entry for every record it names, so the computed fallback only serves
93/// hand-written CIR.
94static llvm::Align recordDeclaredAlign(ModuleOp modOp, cir::RecordType recTy,
95 const DataLayout &dl) {
96 auto layout = cir::tryGetRecordLayout(modOp, recTy.getName());
97 if (!layout)
98 return llvm::Align(dl.getTypeABIAlignment(recTy));
99 return llvm::Align(layout.getRecordAlign());
100}
101
102/// The CIR types the x86_64 bridge handles. Scalars: an integer up to 128
103/// bits (including `_BitInt` and `__int128`), pointer, bool, void, or any
104/// floating-point type. Aggregates: a complete struct or union whose members
105/// are all themselves supported, or an array of a supported element type.
106/// Also a `_Complex` of a supported element type. Everything else is reported
107/// NYI at the reject() choke point in classifyX86_64Function.
108static bool isSupportedType(mlir::Type ty, const DataLayout &dl) {
109 // A pointer is only handled in the default address space (null) or an
110 // already-lowered target address space. A LangAddressSpaceAttr must be
111 // lowered before this pass, so reject it rather than silently dropping it.
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))
116 return true;
117 // Every CIR floating-point type carries the semantics the classifier
118 // switches on, so all of them are handled.
119 if (isa<cir::FPTypeInterface>(ty))
120 return true;
121 if (auto intTy = dyn_cast<cir::IntType>(ty)) {
122 // Integers up to 64 bits, __int128, and _BitInt up to 128 bits are
123 // handled: the classifier extends a width below 32, widens 33 through 63
124 // to i64, coerces 65 through 127 to a {i64, i64} pair, and passes 32, 64,
125 // and 128 in the natural type. A wider _BitInt classifies Indirect,
126 // where at a multiple of 8 the byval attributes the rewriter appends
127 // duplicate the llvm.noundef CIRGen already emitted and trip the
128 // uniqueness assertion on the merged dictionary. The bound is a blanket
129 // 128 because the widths that do not collide reach that same untested
130 // Indirect path. Non-_BitInt intermediate widths (65..127) do not arise
131 // from C. Both stay rejected.
132 if (intTy.getIsBitInt())
133 return intTy.getWidth() <= 128;
134 return intTy.getWidth() <= 64 || intTy.getWidth() == 128;
135 }
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)) {
141 // An incomplete record has no layout to classify, and a packed one needs
142 // pad-aware eightbyte classification this bridge does not implement.
143 if (!recTy.isComplete() || recTy.getPacked())
144 return false;
145 if (recTy.isUnion()) {
146 // The classifier sizes a union's eightbytes from the union itself, which
147 // is only sound when some member spans that size. Short of that, the
148 // remaining bytes are either tail padding or the rest of a bitfield
149 // storage unit, and the CIR type cannot tell those apart even though
150 // classic CodeGen coerces them to i32 and i8 respectively.
151 llvm::ArrayRef<mlir::Type> members = recTy.getMembers();
152 uint64_t recordBits = dl.getTypeSizeInBits(recTy).getFixedValue();
153 if (members.empty()) {
154 // A member-less union is all padding, which classifies Ignore up to two
155 // eightbytes. Past that SysV says MEMORY regardless of content, and
156 // there is no member here to build the Indirect coercion from.
157 if (recordBits > 128)
158 return false;
159 } else {
160 auto spansRecord = [&](mlir::Type m) {
161 return dl.getTypeSizeInBits(m).getFixedValue() == recordBits;
162 };
163 if (!llvm::any_of(members, spansRecord))
164 return false;
165 }
166 } else if (recTy.getPadded()) {
167 // A struct's padding is a member the classifier would have to recognize
168 // as padding rather than data, which is not implemented.
169 return false;
170 }
171 return llvm::all_of(recTy.getMembers(),
172 [&](mlir::Type m) { return isSupportedType(m, dl); });
173 }
174 return false;
175}
176
177/// Convert an llvm::abi::Type coercion type back to a scalar CIR type.
178static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, MLIRContext *ctx) {
179 if (!ty)
180 return nullptr;
181 return llvm::TypeSwitch<const llvm::abi::Type *, mlir::Type>(ty)
182 .Case(
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());
187 })
188 .Case([&](const llvm::abi::FloatType *fltTy) {
189 return cir::getFloatingPointType(*fltTy->getSemantics(), ctx);
190 })
191 .Case([&](const llvm::abi::PointerType *) {
192 return cir::PointerType::get(cir::VoidType::get(ctx));
193 })
194 .Case([&](const llvm::abi::VectorType *vecTy) -> mlir::Type {
195 mlir::Type elemCIR = abiTypeToCIR(vecTy->getElementType(), ctx);
196 if (!elemCIR)
197 return nullptr;
198 return cir::VectorType::get(elemCIR,
199 vecTy->getNumElements().getFixedValue());
200 })
201 .Case([&](const llvm::abi::RecordType *recTy) -> mlir::Type {
202 SmallVector<mlir::Type> fieldTypes;
203 fieldTypes.reserve(recTy->getFields().size());
204 for (const auto &field : recTy->getFields()) {
205 mlir::Type fieldCIR = abiTypeToCIR(field.FieldType, ctx);
206 if (!fieldCIR)
207 return nullptr;
208 fieldTypes.push_back(fieldCIR);
209 }
210 // Coercion types are plain register tuples, not the source record.
211 return cir::StructType::get(ctx, fieldTypes, /*packed=*/false,
212 /*padded=*/false, /*is_class=*/false);
213 })
214 .Default([](const llvm::abi::Type *) -> mlir::Type { return nullptr; });
215}
216
217/// Map a CIR type to an llvm::abi::Type. classifyX86_64Function pre-filters
218/// the signature, so only the scalar and struct/array types handled here can
219/// reach this function.
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());
229 })
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)),
238 addrSpace);
239 })
240 .Case([&](cir::BoolType) {
241 return tb.getIntegerType(dl.getTypeSizeInBits(type),
242 llvm::Align(dl.getTypeABIAlignment(type)),
243 /*Signed=*/false);
244 })
245 .Case([&](cir::VoidType) { return tb.getVoidType(); })
246 .Case([&](cir::FPTypeInterface fpTy) {
247 // LongDoubleType reports its underlying format's semantics, so the
248 // classifier sees x87 or IEEE quad rather than the wrapper.
249 return tb.getFloatType(fpTy.getFloatSemantics(),
250 llvm::Align(dl.getTypeABIAlignment(type)));
251 })
252 .Case([&](cir::ComplexType complexTy) {
253 return tb.getComplexType(
254 mapCIRType(complexTy.getElementType(), typeMapper, dl, modOp),
255 llvm::Align(dl.getTypeABIAlignment(type)));
256 })
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());
262 })
263 .Case([&](cir::RecordType recTy) -> const llvm::abi::Type * {
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);
271 fields.reserve(recTy.getMembers().size());
272
273 // The size passed here spans the tail padding, so an eightbyte covers
274 // the whole union rather than just the member the classifier reduces
275 // it to.
276 if (recTy.isUnion()) {
277 for (mlir::Type fieldTy : recTy.getMembers())
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);
282 }
283
284 // isSupportedType rejects packed and padded structs, so every field
285 // here sits at its naturally-aligned offset.
286 uint64_t offsetBits = 0;
287 for (mlir::Type fieldTy : recTy.getMembers()) {
288 const llvm::abi::Type *mappedField =
289 mapCIRType(fieldTy, typeMapper, dl, modOp);
290 offsetBits =
291 llvm::alignTo(offsetBits, dl.getTypeABIAlignment(fieldTy) * 8);
292 fields.push_back(llvm::abi::FieldInfo(mappedField, offsetBits));
293 offsetBits += dl.getTypeSizeInBits(fieldTy).getFixedValue();
294 }
295 return tb.getRecordType(
296 fields, sizeBits, align, llvm::abi::StructPacking::Default,
297 /*BaseClasses=*/{}, /*VirtualBaseClasses=*/{}, flags);
298 })
299 .Default([](mlir::Type) -> const llvm::abi::Type * {
300 llvm_unreachable(
301 "mapCIRType: type not pre-filtered by classifyX86_64Function");
302 });
303}
304
305/// Convert an llvm::abi::ArgInfo into the ArgClassification consumed by
306/// CIRABIRewriteContext.
307///
308/// Direct: the value passes in register(s). A coercion is forwarded in the
309/// three cases where the value has to be rebuilt on the wire: an aggregate
310/// unpacked into the register(s) holding it, a scalar too wide for one register
311/// split into a tuple of them, and a scalar the classifier widens to fill its
312/// eightbyte. getDirect keeps canFlatten set so the rewriter can split a
313/// multi-field coerced struct into individual wire arguments. Any other scalar
314/// passes in its natural CIR type, which a null coercion denotes. A coercion
315/// this bridge cannot represent yields std::nullopt so the caller reports NYI
316/// rather than silently passing the value unchanged.
317///
318/// Extend: bool or a sub-register integer needs a signext/zeroext attribute.
319/// The x86_64 classifier (llvm/lib/ABI/Targets/X86.cpp) only returns Extend
320/// for an integer or bool operand, so any other origTy is asserted rather
321/// than silently handled.
322///
323/// Indirect: an aggregate that does not fit in registers is passed via a
324/// pointer (sret for returns, byval for arguments).
325///
326/// Ignore: a void return, or a zero-field record dropped from the signature.
327static std::optional<ArgClassification>
328convertABIArgInfo(const llvm::abi::ArgInfo &info, MLIRContext *ctx,
329 mlir::Type origTy) {
330 if (info.isDirect()) {
331 // The classifier names a coerce type even where it matches the natural
332 // type, so a non-null coerce does not by itself mean a rewrite is needed.
333 const llvm::abi::Type *coerceAbi = info.getCoerceToType();
334 bool isAggregate = isa_and_present<cir::RecordType, cir::ArrayType>(origTy);
335 // For a _Complex the classifier's coerce is only sometimes the natural
336 // type, so it has to be read rather than assumed.
337 bool comparesAgainstCoerce =
338 coerceAbi && isa_and_present<cir::ComplexType>(origTy);
339 bool coerceIsRegisterTuple =
340 isa_and_present<llvm::abi::RecordType>(coerceAbi);
341 // Compare widths rather than identity: a coerce no wider than the natural
342 // type carries the same value and needs no rewrite.
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();
349 // Leaving the rest alone also avoids a lossy round trip: abiTypeToCIR
350 // drops the LongDoubleType wrapper and a pointer's pointee, so comparing a
351 // scalar against its own coerce would report a difference that is not one.
352 if (!isAggregate && !comparesAgainstCoerce && !coerceIsRegisterTuple &&
353 !coerceWidensScalar)
354 return ArgClassification::getDirect(nullptr);
355 mlir::Type coerced = abiTypeToCIR(coerceAbi, ctx);
356 if (!coerced)
357 return std::nullopt;
358 // Coercing a value to the type it already has would add a memory round
359 // trip for nothing.
360 if (comparesAgainstCoerce && coerced == origTy)
361 return ArgClassification::getDirect(nullptr);
362 return ArgClassification::getDirect(coerced);
363 }
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());
371 }
372 if (info.isIndirect())
373 return ArgClassification::getIndirect(info.getIndirectAlign(),
374 info.getIndirectByVal());
375 assert(info.isIgnore() && "Unexpected classification");
376 return ArgClassification::getIgnore();
377}
378
379/// Where \p fnTy's declared parameters end and its ellipsis arguments begin.
380///
381/// The only x86_64 rule that reads this boundary sends an unnamed vector wider
382/// than 128 bits to memory, and isSupportedType rejects every vector, so no
383/// input this bridge accepts can observe the difference.
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());
388}
389
390/// Classify an x86_64 SysV signature (return type + argument types) using the
391/// LLVM ABI library. Shared by the cir.func path, the variadic-call path and
392/// the indirect-call path (the latter classifies from the callee function
393/// pointer's pointee FuncType). \p required marks where the declared
394/// parameters in \p inputs end. The classifier treats every argument past that
395/// point as passed through an ellipsis. Returns std::nullopt and emits an NYI
396/// error via \p emitError if the signature uses a type the bridge does not
397/// handle yet.
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);
409
410 auto reject = [&](mlir::Type t) -> bool {
411 if (isSupportedType(t, dl))
412 return false;
413 emitError()
414 << "x86_64 calling-convention lowering not yet implemented for type "
415 << t;
416 return true;
417 };
418 if (!voidRet && reject(retCIR))
419 return std::nullopt;
420 for (mlir::Type a : inputs)
421 if (reject(a))
422 return std::nullopt;
423
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));
430
431 std::unique_ptr<llvm::abi::FunctionInfo> fi = llvm::abi::FunctionInfo::create(
432 llvm::CallingConv::C, retAbi, argAbi, required);
433 targetInfo.computeInfo(*fi);
434
435 // convertABIArgInfo returns nullopt when the classifier picks a coercion this
436 // bridge cannot represent.
437 auto nyiCoercion = [&](mlir::Type t) {
438 emitError() << "x86_64 calling-convention lowering not yet "
439 "implemented for the ABI coercion of type "
440 << t;
441 };
442
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);
448 if (!retAc) {
449 nyiCoercion(retCIR);
450 return std::nullopt;
451 }
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);
457 if (!ac) {
458 nyiCoercion(origArg);
459 return std::nullopt;
460 }
461 fc.argInfos.push_back(*ac);
462 }
463 return fc;
464}
465
466/// Classify a cir.func for x86_64 SysV using the LLVM ABI library. Returns
467/// std::nullopt and emits an NYI error if the signature uses a type the bridge
468/// does not handle yet.
469static std::optional<FunctionClassification>
470classifyX86_64Function(cir::FuncOp func, const DataLayout &dl,
471 mlir::abi::ABITypeMapper &typeMapper,
472 const llvm::abi::TargetInfo &targetInfo,
473 ModuleOp modOp) {
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(); });
479}
480
481/// Classify a call that passes arguments through an ellipsis. The callee's
482/// own classification covers only its declared parameters, but an ellipsis
483/// argument competes for the same argument registers as a declared one, so
484/// what the ABI does with it depends on the whole argument list: the same
485/// small struct is passed in registers early in the list and in memory once
486/// the integer registers are gone. Classifying from the call's operands
487/// rather than the callee's signature is what makes that accounting right.
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(); });
499}
500
501#ifndef NDEBUG
502/// Whether \p callFc classifies a call's leading arguments and its return
503/// exactly as \p calleeFc classifies the callee's declared parameters and
504/// return. A function definition and its call sites are rewritten from
505/// separate classifications, so the two would silently disagree on the wire
506/// format if an ellipsis argument could ever change how a declared parameter
507/// is passed.
508static bool classifiesSamePrefix(const FunctionClassification &calleeFc,
509 const FunctionClassification &callFc) {
510 if (callFc.argInfos.size() < calleeFc.argInfos.size())
511 return false;
512 return calleeFc.returnInfo == callFc.returnInfo &&
513 std::equal(calleeFc.argInfos.begin(), calleeFc.argInfos.end(),
514 callFc.argInfos.begin());
515}
516#endif
517
518struct CallConvLoweringPass
519 : public impl::CallConvLoweringBase<CallConvLoweringPass> {
520 using CallConvLoweringBase::CallConvLoweringBase;
521
522 CallConvLoweringPass(const CallConvLoweringOptions &options,
523 const llvm::abi::ABICompatInfo &x86AbiCompat)
524 : CallConvLoweringBase(options), x86AbiCompat(x86AbiCompat) {}
525
526 void runOnOperation() override;
527
528 /// The x86_64 flags whose value depends on the target and the requested ABI
529 /// compatibility version. Carried outside the pass options because the
530 /// struct has no command-line parser, so a cir-opt run gets the library
531 /// defaults rather than a target's values.
532 llvm::abi::ABICompatInfo x86AbiCompat;
533};
534
535/// Record on \p fc whether \p returnType is CIR's void. The x86_64 classifier
536/// answers this itself, but the other two drivers cannot: the test target is
537/// dialect-neutral and has no notion of CIR's void, and the
538/// classification-attr schema carries no return type at all. Both route
539/// through here so a classification always reaches needsRewrite paired with
540/// the return type it was built from.
541static std::optional<FunctionClassification>
542withReturnVoidness(std::optional<FunctionClassification> fc,
543 mlir::Type returnType) {
544 if (fc)
545 fc->returnsVoid = mlir::isa<cir::VoidType>(returnType);
546 return fc;
547}
548
549/// Classify \p func using whichever driver mode is configured. Returns
550/// std::nullopt and emits an error on the function if classification fails
551/// (e.g. injection-driver mode but the function is missing the attribute,
552/// or the attribute is malformed).
553std::optional<FunctionClassification>
554classifyFunction(cir::FuncOp func, const DataLayout &dl,
555 cir::CallConvTarget target, StringRef classificationAttrName) {
556 ArrayRef<Type> argTypes = func.getFunctionType().getInputs();
557 Type returnType = func.getFunctionType().getReturnType();
558
559 if (!classificationAttrName.empty()) {
560 auto attr = func->getAttrOfType<DictionaryAttr>(classificationAttrName);
561 if (!attr) {
562 func.emitOpError()
563 << "missing classification attribute '" << classificationAttrName
564 << "' (CallConvLowering driver mode 'classification-attr')";
565 return std::nullopt;
566 }
567 return withReturnVoidness(mlir::abi::test::parseClassificationAttr(
568 attr, [&]() { return func.emitOpError(); }),
569 returnType);
570 }
571
572 // The x86_64 target is handled directly in runOnOperation (it needs a shared
573 // ABITypeMapper and TargetInfo), so only the test target reaches here.
574 assert(target == cir::CallConvTarget::Test &&
575 "classifyFunction only handles the test target");
576 return withReturnVoidness(mlir::abi::test::classify(argTypes, returnType, dl),
577 returnType);
578}
579
580/// Find the cir.func declaration matching a direct cir.call / cir.try_call
581/// callee, if any. Returns nullptr if the callee is indirect or the symbol
582/// cannot be resolved. Takes a SymbolTable instead of a ModuleOp so the
583/// symbol lookup is amortized across all the call sites the driver walks
584/// (ModuleOp::lookupSymbol is linear per call).
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();
591 else
592 return nullptr;
593 if (!callee)
594 return nullptr;
595 return symbolTable.lookup<cir::FuncOp>(callee.getValue());
596}
597
598/// The signature an indirect call reaches its callee through, or a null type
599/// for a direct call. The callee's pointer-to-function shape is asserted
600/// rather than verified: the dialect checks operand types against the callee
601/// only for a direct call, so IR that breaks it fails here instead of in the
602/// verifier.
603cir::FuncType indirectCalleeType(cir::CIRCallOpInterface call) {
604 if (!call.isIndirect())
605 return {};
606 return cast<cir::FuncType>(
607 cast<cir::PointerType>(call.getIndirectCall().getType()).getPointee());
608}
609
610void CallConvLoweringPass::runOnOperation() {
611 ModuleOp moduleOp = getOperation();
612 MLIRContext *ctx = &getContext();
613
614 bool haveTarget = target != cir::CallConvTarget::None;
615 bool haveAttr = !classificationAttr.empty();
616 if (haveTarget == haveAttr) {
617 moduleOp.emitOpError() << "CallConvLowering requires exactly one of "
618 "'target' or 'classification-attr' pass options";
619 signalPassFailure();
620 return;
621 }
622
623 if (!moduleOp->hasAttr(DLTIDialect::kDataLayoutAttrName)) {
624 moduleOp.emitOpError()
625 << "CallConvLowering requires a DataLayout (dlti.dl_spec attribute "
626 "on the module)";
627 signalPassFailure();
628 return;
629 }
630
631 DataLayout dl(moduleOp);
632 CIRABIRewriteContext rewriteCtx(moduleOp, dl);
633 SymbolTable symbolTable(moduleOp);
634
635 // For the x86_64 target, build the LLVM ABI library classifier once and
636 // reuse it (and its type mapper) across every function.
637 std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
638 std::unique_ptr<llvm::abi::TargetInfo> x86Target;
639 if (target == cir::CallConvTarget::X86_64) {
640 x86TypeMapper.emplace(dl);
641 x86Target = llvm::abi::createX86_64TargetInfo(
642 x86TypeMapper->getTypeBuilder(), x86AvxAbiLevel.getValue(),
643 /*Has64BitPointers=*/true, x86AbiCompat);
644 }
645
646 // Classify every cir.func up front. No IR mutation happens here, so
647 // later walks can consult any function's classification regardless of
648 // visitation order.
649 llvm::MapVector<cir::FuncOp, FunctionClassification> classifications;
650 bool anyFailed = false;
651 moduleOp.walk([&](cir::FuncOp f) {
652 std::optional<FunctionClassification> fc;
653 if (x86Target)
654 fc = classifyX86_64Function(f, dl, *x86TypeMapper, *x86Target, moduleOp);
655 else
656 fc = classifyFunction(f, dl, target, classificationAttr);
657 if (!fc) {
658 anyFailed = true;
659 return;
660 }
661 classifications.insert({f, std::move(*fc)});
662 });
663 if (anyFailed) {
664 signalPassFailure();
665 return;
666 }
667
668 // Build a callee-to-callers index. One module walk collects every direct
669 // cir.call / cir.try_call to each cir.func; the loop below rewrites a
670 // function and all of its call sites together. Indirect or unresolved
671 // callees are skipped here; rewriteCallSite errors on those at the end.
672 //
673 // A call that passes arguments through an ellipsis gets its own
674 // classification, recorded here while every signature is still in its
675 // original form. The callee's classification covers only its declared
676 // parameters and cannot describe those extra arguments.
677 llvm::DenseMap<cir::FuncOp, SmallVector<Operation *>> callers;
678 // Keyed on the call op collected below, looked up once when that same op is
679 // rewritten. A key must never come from an op created during the rewrite:
680 // a recycled address could match an unrelated entry.
681 llvm::DenseMap<Operation *, FunctionClassification> variadicCallSites;
682 moduleOp.walk([&](Operation *op) {
683 auto call = dyn_cast<cir::CIRCallOpInterface>(op);
684 if (!call)
685 return;
686 cir::FuncOp callee = lookupCallee(op, symbolTable);
687 if (!callee)
688 return;
689 callers[callee].push_back(op);
690
691 // Only the x86_64 driver classifies per call site. Under the other
692 // drivers the classification comes from a fixed per-function source, so
693 // such a call stays short a classification and rewriteCallSite reports it.
694 cir::FuncType calleeTy = callee.getFunctionType();
695 if (!x86Target || call.getNumArgOperands() <= calleeTy.getNumInputs())
696 return;
697 // A callee declared without a prototype also takes more operands than it
698 // declares, and the verifier allows it. Those extra arguments are named
699 // rather than passed through an ellipsis, so the accounting below does not
700 // describe them.
701 if (!calleeTy.isVarArg()) {
702 op->emitOpError() << "extra arguments to a callee without a prototype "
703 "not yet implemented in CallConvLowering";
704 anyFailed = true;
705 return;
706 }
707 std::optional<FunctionClassification> fc = classifyX86_64VariadicCall(
708 call, calleeTy, dl, *x86TypeMapper, *x86Target, moduleOp);
709 if (!fc) {
710 anyFailed = true;
711 return;
712 }
713 variadicCallSites.insert({op, std::move(*fc)});
714 });
715 if (anyFailed) {
716 signalPassFailure();
717 return;
718 }
719
720 // A cir.get_global holding a function's address carries the signature the
721 // source wrote, which the verifier ties to the callee, so it goes stale when
722 // that callee is rewritten. A function address in a global initializer is a
723 // GlobalViewAttr instead, whose recorded type the verifier does not tie to
724 // the callee and which is dropped at opaque-pointer lowering, so it needs no
725 // counterpart.
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()))
730 return;
731 // A get_global's pointee must equal the named symbol's type, and the
732 // GlobalOp verifier rejects a function type there, so this names a
733 // cir.func.
734 auto callee = cast<cir::FuncOp>(symbolTable.lookup(getGlobal.getName()));
735 addressTakers[callee].push_back(getGlobal);
736 });
737
738 // Rewrite each function together with every direct call to it and every op
739 // holding its address. By the time we move on to function F+1, F's
740 // signature and every reference to F have already been brought into
741 // alignment, and F+1..FN are still in their original (mutually consistent)
742 // form, so the IR is verifier-clean at every outer-iteration boundary.
743 //
744 // There is still a brief inner window where F's signature has been
745 // rewritten but its references have not yet caught up -- we have no way to
746 // mutate both sides of a call atomically. No verifier runs inside the
747 // pass, and at pass exit the module is verifier-clean. Fusing the inner
748 // loops here keeps the invalid window per-function rather than module-wide.
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))) {
754 signalPassFailure();
755 return;
756 }
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");
765 }
766 if (failed(rewriteCtx.rewriteCallSite(callOp, *callFc, builder))) {
767 signalPassFailure();
768 return;
769 }
770 }
771 for (cir::GetGlobalOp addrOp : addressTakers.lookup(func))
772 rewriteCtx.rewriteFunctionAddress(addrOp, func, builder);
773 }
774
775 // Rewrite indirect call sites. The callee is opaque, so classify from the
776 // function pointer's pointee FuncType and let rewriteCallSite retype the
777 // callee pointer to match the coerced signature. Collect the calls first:
778 // when an sret rewrite reuses a single-use store's destination as the return
779 // slot it erases that store, which is the operation a live walk has already
780 // cached as the next one to visit.
781 SmallVector<cir::CIRCallOpInterface> indirectCalls;
782 moduleOp.walk([&](cir::CIRCallOpInterface c) {
783 cir::FuncType calleeTy = indirectCalleeType(c);
784 if (!calleeTy)
785 return;
786 // A cir.try_call is in this walk so that a variadic one reaches the
787 // ellipsis accounting below. CIRABIRewriteContext cannot rebuild a
788 // cir.try_call at all, so a non-variadic one has never been rewritten
789 // here. Keep it out rather than start reporting a gap that has nothing
790 // to do with the ellipsis.
791 if (!calleeTy.isVarArg() && isa<cir::TryCallOp>(c.getOperation()))
792 return;
793 indirectCalls.push_back(c);
794 });
795 for (cir::CIRCallOpInterface c : indirectCalls) {
796 // classification-attr mode injects a per-function classification, which
797 // cannot describe a callee resolved at run time. Report it rather than
798 // leave the indirect call unrewritten while direct calls are coerced.
799 if (!classificationAttr.empty()) {
800 c->emitOpError() << "indirect call cannot be classified in the "
801 "'classification-attr' driver mode";
802 signalPassFailure();
803 return;
804 }
805 cir::FuncType funcTy = indirectCalleeType(c);
806 auto classifySignature =
807 [&](mlir::TypeRange argTypes) -> std::optional<FunctionClassification> {
808 if (x86Target)
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());
816 };
817
818 // An argument passed through an ellipsis has no counterpart in the
819 // pointee's parameter list, so classify the call's own operands to learn
820 // what the ABI does with it. If nothing in the full list needs a rewrite
821 // the call already carries its wire form and can stand as written.
822 // Anything else needs a rewrite the pointee's signature cannot describe,
823 // since it has no entry for the arguments past the ellipsis.
824 if (c.getNumArgOperands() > funcTy.getNumInputs()) {
825 std::optional<FunctionClassification> callFc =
826 classifySignature(c.getArgOperands().getTypes());
827 if (!callFc) {
828 signalPassFailure();
829 return;
830 }
831 if (!callFc->needsRewrite())
832 continue;
833 c->emitOpError() << "variadic arguments to an indirect call not yet "
834 "implemented in CallConvLowering";
835 signalPassFailure();
836 return;
837 }
838
839 std::optional<FunctionClassification> fc =
840 classifySignature(funcTy.getInputs());
841 if (!fc) {
842 signalPassFailure();
843 return;
844 }
845 if (failed(rewriteCtx.rewriteCallSite(c.getOperation(), *fc, builder))) {
846 signalPassFailure();
847 return;
848 }
849 }
850}
851
852} // namespace
853
854std::unique_ptr<Pass> mlir::createCallConvLoweringPass() {
855 return std::make_unique<CallConvLoweringPass>();
856}
857
858std::unique_ptr<Pass>
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);
866}
C++ view class that accepts both !cir.struct and !cir.union types.
Definition CIRTypes.h:103
bool isUnion() const
Definition CIRTypes.h:128
bool isComplete() const
Definition CIRTypes.h:122
llvm::ArrayRef< mlir::Type > getMembers() const
Definition CIRTypes.cpp:519
bool getPacked() const
Definition CIRTypes.cpp:534
mlir::StringAttr getName() const
Definition CIRTypes.cpp:524
bool getPadded() const
Definition CIRTypes.cpp:539
RecordLayoutAttr tryGetRecordLayout(mlir::ModuleOp mod, mlir::StringAttr name)
Same lookup as getRecordLayout, but returns a null attribute instead of asserting when the record has...
Definition CIRAttrs.cpp:923
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...
Definition CIRTypes.cpp:42
CallConvTarget
The ABI target whose calling-convention rules drive CallConvLowering.
Definition Passes.h:23
const internal::VariadicAllOfMatcher< Attr > attr
constexpr size_t align(size_t Size)
Aligns a size to the pointer alignment.
Definition PrimType.h:201
void info(bool Verbose, unsigned Level, const char *Fmt, Ts &&...Args)
Prints an indented note to stderr when Verbose is set.
Definition Utils.h:56
@ Default
Set to the current date and time.
std::unique_ptr< Pass > createCallConvLoweringPass()