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 (scalar and struct/array types)
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 / pointer / bool / f32 / f64 scalars and struct / array
71// aggregates are handled; unions, `_BitInt`, `_Complex`, vectors, wider
72// floats, and packed or padded records are reported NYI by
73// classifyX86_64Function so an unsupported signature fails the pass instead of
74// 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 mlir::StringAttr name = recTy.getName();
83 if (!name)
84 return true;
85 auto dict = modOp->getAttrOfType<DictionaryAttr>(
86 cir::CIRDialect::getRecordLayoutsAttrName());
87 if (!dict)
88 return true;
89 auto layout = dict.getAs<cir::RecordLayoutAttr>(name);
90 if (!layout)
91 return true;
92 return layout.getArgPassingKind() == cir::ArgPassingKind::CanPassInRegs;
93}
94
95/// The CIR types the x86_64 bridge handles. Scalars: a regular integer up to
96/// 64 bits, pointer, bool, void, f32, or f64. Aggregates: a complete struct
97/// whose fields are all themselves supported, or an array of a supported
98/// element type. `_BitInt`, `__int128`, unions, `_Complex`, vectors, wider
99/// floats, and packed or padded records are not handled and are reported NYI
100/// at the reject() choke point in classifyX86_64Function.
101static bool isSupportedType(mlir::Type ty) {
102 // A pointer is only handled in the default address space (null) or an
103 // already-lowered target address space. A LangAddressSpaceAttr must be
104 // lowered before this pass, so reject it rather than silently dropping it.
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))
109 return true;
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)) {
115 // Unions and packed / padded records each need classification this bridge
116 // does not implement (a union widen fixup and pad-aware eightbyte
117 // classification), so reject them here and report NYI rather than
118 // misclassify. A zero-field record (a C empty struct) classifies as
119 // Ignore and is dropped from the lowered signature. CIRGen lays out an
120 // empty C++ class as a single padded byte, which the padded check rejects.
121 // A real one-byte struct such as `{char[1]}` has a field and is not
122 // padded, so it is classified normally.
123 if (recTy.isUnion() || !recTy.isComplete() || recTy.getPacked() ||
124 recTy.getPadded())
125 return false;
126 return llvm::all_of(recTy.getMembers(),
127 [](mlir::Type m) { return isSupportedType(m); });
128 }
129 return false;
130}
131
132/// Convert an llvm::abi::Type coercion type back to a scalar CIR type.
133static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, MLIRContext *ctx) {
134 if (!ty)
135 return nullptr;
136 return llvm::TypeSwitch<const llvm::abi::Type *, mlir::Type>(ty)
137 .Case(
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(),
141 intTy->isSigned());
142 })
143 .Case([&](const llvm::abi::FloatType *fltTy) {
144 return cir::getFloatingPointType(*fltTy->getSemantics(), ctx);
145 })
146 .Case([&](const llvm::abi::PointerType *) {
147 return cir::PointerType::get(cir::VoidType::get(ctx));
148 })
149 .Case([&](const llvm::abi::RecordType *recTy) -> mlir::Type {
150 SmallVector<mlir::Type> fieldTypes;
151 fieldTypes.reserve(recTy->getFields().size());
152 for (const auto &field : recTy->getFields()) {
153 mlir::Type fieldCIR = abiTypeToCIR(field.FieldType, ctx);
154 if (!fieldCIR)
155 return nullptr;
156 fieldTypes.push_back(fieldCIR);
157 }
158 // Coercion types are plain register tuples, not the source record.
159 return cir::StructType::get(ctx, fieldTypes, /*packed=*/false,
160 /*padded=*/false, /*is_class=*/false);
161 })
162 .Default([](const llvm::abi::Type *) -> mlir::Type { return nullptr; });
163}
164
165/// Map a CIR type to an llvm::abi::Type. classifyX86_64Function pre-filters
166/// the signature, so only the scalar and struct/array types handled here can
167/// reach this function.
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)),
176 intTy.isSigned());
177 })
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)),
186 addrSpace);
187 })
188 .Case([&](cir::BoolType) {
189 return tb.getIntegerType(dl.getTypeSizeInBits(type),
190 llvm::Align(dl.getTypeABIAlignment(type)),
191 /*Signed=*/false);
192 })
193 .Case([&](cir::VoidType) { return tb.getVoidType(); })
194 .Case([&](cir::SingleType) {
195 return tb.getFloatType(llvm::APFloat::IEEEsingle(),
196 llvm::Align(dl.getTypeABIAlignment(type)));
197 })
198 .Case([&](cir::DoubleType) {
199 return tb.getFloatType(llvm::APFloat::IEEEdouble(),
200 llvm::Align(dl.getTypeABIAlignment(type)));
201 })
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());
207 })
208 .Case([&](cir::RecordType recTy) -> const llvm::abi::Type * {
209 // isSupportedType rejects unions, packed / padded, and empty-for-ABI
210 // records, so this handles a plain struct: map each field at its
211 // naturally-aligned offset.
213 fields.reserve(recTy.getMembers().size());
214 uint64_t offsetBits = 0;
215 for (mlir::Type fieldTy : recTy.getMembers()) {
216 const llvm::abi::Type *mappedField =
217 mapCIRType(fieldTy, typeMapper, dl, modOp);
218 offsetBits =
219 llvm::alignTo(offsetBits, dl.getTypeABIAlignment(fieldTy) * 8);
220 fields.push_back(llvm::abi::FieldInfo(mappedField, offsetBits));
221 offsetBits += dl.getTypeSizeInBits(fieldTy).getFixedValue();
222 }
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,
231 /*BaseClasses=*/{}, /*VirtualBaseClasses=*/{},
232 flags);
233 })
234 .Default([](mlir::Type) -> const llvm::abi::Type * {
235 llvm_unreachable(
236 "mapCIRType: type not pre-filtered by classifyX86_64Function");
237 });
238}
239
240/// Convert an llvm::abi::ArgInfo into the ArgClassification consumed by
241/// CIRABIRewriteContext.
242///
243/// Direct: a scalar passes as-is (nullptr coercion means "same as the
244/// original CIR type"). A struct or array is coerced to a register-friendly
245/// type; getDirect keeps canFlatten set so the rewriter can split a
246/// multi-field coerced struct into individual wire arguments. If the
247/// classifier picks a coercion this bridge cannot represent (e.g. an SSE
248/// <2 x float> vector), std::nullopt is returned so the caller reports NYI
249/// rather than silently passing the aggregate unchanged.
250///
251/// Extend: bool or a sub-register integer needs a signext/zeroext attribute.
252/// Every ArgInfo::getExtend() call site in the x86_64 classifier
253/// (llvm/lib/ABI/Targets/X86.cpp) is gated on the operand being an integer,
254/// so a non-integer, non-bool origTy here would mean the classifier
255/// disagreed with its own source -- asserted rather than silently handled.
256///
257/// Indirect: an aggregate that does not fit in registers is passed via a
258/// pointer (sret for returns, byval for arguments).
259///
260/// Ignore: a void return has no register or stack slot, and a zero-field
261/// (empty) record is dropped from the signature.
262static std::optional<ArgClassification>
263convertABIArgInfo(const llvm::abi::ArgInfo &info, MLIRContext *ctx,
264 mlir::Type origTy) {
265 if (info.isDirect()) {
266 // A scalar passes as-is; only an aggregate carries a coercion type.
267 if (!origTy || !isa<cir::RecordType, cir::ArrayType>(origTy))
268 return ArgClassification::getDirect(nullptr);
269 // An aggregate must coerce to a type this bridge can represent. A coerce
270 // this bridge cannot map (an SSE vector, or a nested type it does not
271 // handle) yields a null type; report that as NYI instead of leaving the
272 // aggregate as an unchanged by-value record.
273 mlir::Type coerced = abiTypeToCIR(info.getCoerceToType(), ctx);
274 if (!coerced)
275 return std::nullopt;
276 return ArgClassification::getDirect(coerced);
277 }
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());
285 }
286 if (info.isIndirect())
287 return ArgClassification::getIndirect(info.getIndirectAlign(),
288 info.getIndirectByVal());
289 assert(info.isIgnore() && "Unexpected classification");
290 return ArgClassification::getIgnore();
291}
292
293/// Classify a cir.func for x86_64 SysV using the LLVM ABI library. Returns
294/// std::nullopt and emits an NYI error if the signature uses a type the bridge
295/// does not handle yet.
296static std::optional<FunctionClassification>
297classifyX86_64Function(cir::FuncOp func, const DataLayout &dl,
298 mlir::abi::ABITypeMapper &typeMapper,
299 const llvm::abi::TargetInfo &targetInfo,
300 ModuleOp modOp) {
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);
306
307 auto reject = [&](mlir::Type t) -> bool {
308 if (isSupportedType(t))
309 return false;
310 func.emitOpError()
311 << "x86_64 calling-convention lowering not yet implemented for type "
312 << t;
313 return true;
314 };
315 if (!voidRet && reject(retCIR))
316 return std::nullopt;
317 for (mlir::Type a : fnTy.getInputs())
318 if (reject(a))
319 return std::nullopt;
320
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));
327
328 std::unique_ptr<llvm::abi::FunctionInfo> fi =
329 llvm::abi::FunctionInfo::create(llvm::CallingConv::C, retAbi, argAbi);
330 targetInfo.computeInfo(*fi);
331
332 // convertABIArgInfo returns nullopt when the classifier picks a coercion
333 // this bridge cannot represent (e.g. an SSE vector coerce for an all-float
334 // aggregate). Report it as NYI rather than emitting a wrong signature.
335 auto nyiCoercion = [&](mlir::Type t) {
336 func.emitOpError() << "x86_64 calling-convention lowering not yet "
337 "implemented for the ABI coercion of type "
338 << t;
339 };
340
341 FunctionClassification fc;
342 mlir::Type origRet = voidRet ? mlir::Type() : retCIR;
343 std::optional<ArgClassification> retAc =
344 convertABIArgInfo(fi->getReturnInfo(), ctx, origRet);
345 if (!retAc) {
346 nyiCoercion(retCIR);
347 return std::nullopt;
348 }
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);
355 if (!ac) {
356 nyiCoercion(origArg);
357 return std::nullopt;
358 }
359 fc.argInfos.push_back(*ac);
360 }
361 return fc;
362}
363
364bool needsRewrite(const FunctionClassification &fc) {
365 if ((fc.returnInfo.kind != ArgKind::Direct) || fc.returnInfo.coercedType)
366 return true;
367 for (const ArgClassification &ac : fc.argInfos)
368 if ((ac.kind != ArgKind::Direct) || ac.coercedType)
369 return true;
370 return false;
371}
372
373struct CallConvLoweringPass
374 : public impl::CallConvLoweringBase<CallConvLoweringPass> {
375 using CallConvLoweringBase::CallConvLoweringBase;
376 void runOnOperation() override;
377};
378
379/// Classify \p func using whichever driver mode is configured. Returns
380/// std::nullopt and emits an error on the function if classification fails
381/// (e.g. injection-driver mode but the function is missing the attribute,
382/// or the attribute is malformed).
383std::optional<FunctionClassification>
384classifyFunction(cir::FuncOp func, const DataLayout &dl,
385 cir::CallConvTarget target, StringRef classificationAttrName) {
386 ArrayRef<Type> argTypes = func.getFunctionType().getInputs();
387 Type returnType = func.getFunctionType().getReturnType();
388
389 if (!classificationAttrName.empty()) {
390 auto attr = func->getAttrOfType<DictionaryAttr>(classificationAttrName);
391 if (!attr) {
392 func.emitOpError()
393 << "missing classification attribute '" << classificationAttrName
394 << "' (CallConvLowering driver mode 'classification-attr')";
395 return std::nullopt;
396 }
397 return mlir::abi::test::parseClassificationAttr(
398 attr, [&]() { return func.emitOpError(); });
399 }
400
401 // The x86_64 target is handled directly in runOnOperation (it needs a shared
402 // ABITypeMapper and TargetInfo), so only the test target reaches here.
403 assert(target == cir::CallConvTarget::Test &&
404 "classifyFunction only handles the test target");
405 return mlir::abi::test::classify(argTypes, returnType, dl);
406}
407
408/// Find the cir.func declaration matching a direct cir.call / cir.try_call
409/// callee, if any. Returns nullptr if the callee is indirect or the symbol
410/// cannot be resolved. Takes a SymbolTable instead of a ModuleOp so the
411/// symbol lookup is amortized across all the call sites the driver walks
412/// (ModuleOp::lookupSymbol is linear per call).
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();
419 else
420 return nullptr;
421 if (!callee)
422 return nullptr;
423 return symbolTable.lookup<cir::FuncOp>(callee.getValue());
424}
425
426void CallConvLoweringPass::runOnOperation() {
427 ModuleOp moduleOp = getOperation();
428 MLIRContext *ctx = &getContext();
429
430 bool haveTarget = target != cir::CallConvTarget::None;
431 bool haveAttr = !classificationAttr.empty();
432 if (haveTarget == haveAttr) {
433 moduleOp.emitOpError() << "CallConvLowering requires exactly one of "
434 "'target' or 'classification-attr' pass options";
435 signalPassFailure();
436 return;
437 }
438
439 if (!moduleOp->hasAttr(DLTIDialect::kDataLayoutAttrName)) {
440 moduleOp.emitOpError()
441 << "CallConvLowering requires a DataLayout (dlti.dl_spec attribute "
442 "on the module)";
443 signalPassFailure();
444 return;
445 }
446
447 DataLayout dl(moduleOp);
448 CIRABIRewriteContext rewriteCtx(moduleOp, dl);
449 SymbolTable symbolTable(moduleOp);
450
451 // For the x86_64 target, build the LLVM ABI library classifier once and
452 // reuse it (and its type mapper) across every function.
453 std::optional<mlir::abi::ABITypeMapper> x86TypeMapper;
454 std::unique_ptr<llvm::abi::TargetInfo> x86Target;
455 if (target == cir::CallConvTarget::X86_64) {
456 x86TypeMapper.emplace(dl);
457 x86Target = llvm::abi::createX86_64TargetInfo(
458 x86TypeMapper->getTypeBuilder(), x86AvxAbiLevel.getValue(),
459 /*Has64BitPointers=*/true, llvm::abi::ABICompatInfo());
460 }
461
462 // Classify every cir.func up front. No IR mutation happens here, so
463 // later walks can consult any function's classification regardless of
464 // visitation order.
465 llvm::MapVector<cir::FuncOp, FunctionClassification> classifications;
466 bool anyFailed = false;
467 moduleOp.walk([&](cir::FuncOp f) {
468 std::optional<FunctionClassification> fc;
469 if (x86Target)
470 fc = classifyX86_64Function(f, dl, *x86TypeMapper, *x86Target, moduleOp);
471 else
472 fc = classifyFunction(f, dl, target, classificationAttr);
473 if (!fc) {
474 anyFailed = true;
475 return;
476 }
477 classifications.insert({f, std::move(*fc)});
478 });
479 if (anyFailed) {
480 signalPassFailure();
481 return;
482 }
483
484 // Build a callee-to-callers index. One module walk collects every direct
485 // cir.call / cir.try_call to each cir.func; the loop below rewrites a
486 // function and all of its call sites together. Indirect or unresolved
487 // callees are skipped here; rewriteCallSite errors on those at the end.
488 llvm::DenseMap<cir::FuncOp, SmallVector<Operation *>> callers;
489 moduleOp.walk([&](Operation *op) {
490 if (!isa<cir::CallOp, cir::TryCallOp>(op))
491 return;
492 if (cir::FuncOp callee = lookupCallee(op, symbolTable))
493 callers[callee].push_back(op);
494 });
495
496 // Rewrite each function together with every direct call to it. By the
497 // time we move on to function F+1, F's signature and every direct call to
498 // F have already been brought into alignment, and F+1..FN are still in
499 // their original (mutually consistent) form, so the IR is verifier-clean
500 // at every outer-iteration boundary.
501 //
502 // There is still a brief inner window where F's signature has been
503 // rewritten but its callers have not yet caught up -- we have no way to
504 // mutate both sides of a call atomically. No verifier runs inside the
505 // pass, and at pass exit the module is verifier-clean. Fusing the inner
506 // loop here keeps the invalid window per-function rather than module-wide.
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))) {
512 signalPassFailure();
513 return;
514 }
515 for (Operation *callOp : callers.lookup(func)) {
516 if (failed(rewriteCtx.rewriteCallSite(callOp, fc, builder))) {
517 signalPassFailure();
518 return;
519 }
520 }
521 }
522
523 // Reject indirect calls when the module contains any ABI rewrite that
524 // would need call-site lowering. We cannot strip or coerce operands
525 // without a resolved callee symbol.
526 const FunctionClassification *rewriteFc = nullptr;
527 for (auto &kv : classifications) {
528 if (needsRewrite(kv.second)) {
529 rewriteFc = &kv.second;
530 break;
531 }
532 }
533 if (rewriteFc) {
534 moduleOp.walk([&](cir::CallOp c) {
535 if (!c.isIndirect())
536 return;
537 if (failed(rewriteCtx.rewriteCallSite(c, *rewriteFc, builder)))
538 anyFailed = true;
539 });
540 if (anyFailed) {
541 signalPassFailure();
542 return;
543 }
544 }
545}
546
547} // namespace
548
549std::unique_ptr<Pass> mlir::createCallConvLoweringPass() {
550 return std::make_unique<CallConvLoweringPass>();
551}
552
553std::unique_ptr<Pass>
555 llvm::abi::X86AVXABILevel x86AvxAbiLevel) {
556 CallConvLoweringOptions options;
557 options.target = target;
558 options.x86AvxAbiLevel = x86AvxAbiLevel;
559 return std::make_unique<CallConvLoweringPass>(options);
560}
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
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
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
@ Default
Set to the current date and time.
std::unique_ptr< Pass > createCallConvLoweringPass()