clang 24.0.0git
LowerToLLVM.cpp
Go to the documentation of this file.
1//====- LowerToLLVM.cpp - Lowering from CIR to LLVMIR ---------------------===//
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 file implements lowering of CIR operations to LLVMIR.
10//
11//===----------------------------------------------------------------------===//
12
13#include "LowerToLLVM.h"
14
15#include <array>
16#include <optional>
17
18#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
19#include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h"
20#include "mlir/Dialect/DLTI/DLTI.h"
21#include "mlir/Dialect/Func/IR/FuncOps.h"
22#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
23#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
24#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
25#include "mlir/Dialect/OpenMP/Transforms/Passes.h"
26#include "mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h"
27#include "mlir/IR/BuiltinAttributes.h"
28#include "mlir/IR/BuiltinDialect.h"
29#include "mlir/IR/BuiltinOps.h"
30#include "mlir/IR/Location.h"
31#include "mlir/IR/Types.h"
32#include "mlir/Pass/Pass.h"
33#include "mlir/Pass/PassManager.h"
34#include "mlir/Support/LLVM.h"
35#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
36#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
37#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
38#include "mlir/Target/LLVMIR/Export.h"
39#include "mlir/Transforms/DialectConversion.h"
47#include "clang/CIR/Passes.h"
48#include "llvm/ADT/MapVector.h"
49#include "llvm/ADT/StringMap.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/IR/Module.h"
52#include "llvm/Support/Casting.h"
53#include "llvm/Support/ErrorHandling.h"
54#include "llvm/Support/TimeProfiler.h"
55#include "llvm/Support/VirtualFileSystem.h"
56#include "llvm/Support/raw_ostream.h"
57
58using namespace cir;
59using namespace llvm;
60
61namespace cir {
62namespace direct {
63
64//===----------------------------------------------------------------------===//
65// Helper Methods
66//===----------------------------------------------------------------------===//
67
68namespace {
69/// If the given type is a vector type, return the vector's element type.
70/// Otherwise return the given type unchanged.
71mlir::Type elementTypeIfVector(mlir::Type type) {
72 return llvm::TypeSwitch<mlir::Type, mlir::Type>(type)
73 .Case<cir::VectorType, mlir::VectorType>(
74 [](auto p) { return p.getElementType(); })
75 .Default([](mlir::Type p) { return p; });
76}
77} // namespace
78
79/// In-memory storage width in bits for a _BitInt(N): N rounded up to the type's
80/// ABI alignment. This equals sizeof(_BitInt(N)) * 8 on the default target
81/// (e.g. _BitInt(6) -> 8, _BitInt(17) -> 32, _BitInt(128) -> 128).
82static unsigned getBitIntMemoryStorageBits(cir::IntType ty,
83 const mlir::DataLayout &dataLayout) {
84 uint64_t alignBits = ty.getABIAlignment(dataLayout, {}) * 8;
85 return llvm::alignTo(ty.getWidth(), alignBits);
86}
87
88/// A _BitInt(N) whose padded storage integer iM has a larger alloc size than
89/// its M/8 store size is laid out by clang as a byte array, not a plain integer
90/// (e.g. _BitInt(129) -> i192 with alloc size 32 != store size 24). That
91/// "split" storage form is not yet implemented; lowerings must detect it and
92/// report errorNYI rather than emit the wrong-sized integer.
93static bool isSplitStorageBitInt(cir::IntType ty,
94 const mlir::DataLayout &dataLayout) {
95 if (!ty.isBitInt())
96 return false;
97 unsigned storageBits = getBitIntMemoryStorageBits(ty, dataLayout);
98 auto storageTy = mlir::IntegerType::get(ty.getContext(), storageBits);
99 uint64_t storeSize = storageBits / 8;
100 uint64_t allocSize =
101 llvm::alignTo(storeSize, dataLayout.getTypeABIAlignment(storageTy));
102 return allocSize != storeSize;
103}
104
105/// Given a type convertor and a data layout, convert the given type to a type
106/// that is suitable for memory operations. For example, this can be used to
107/// lower cir.bool accesses to i8.
108static mlir::Type convertTypeForMemory(const mlir::TypeConverter &converter,
109 mlir::DataLayout const &dataLayout,
110 mlir::Type type) {
111 // TODO(cir): Handle other types similarly to clang's codegen
112 // convertTypeForMemory
113 if (isa<cir::BoolType>(type)) {
114 return mlir::IntegerType::get(type.getContext(),
115 dataLayout.getTypeSizeInBits(type));
116 }
117
118 // _BitInt(N) keeps its literal width as a value but is stored in a padded
119 // integer iM in memory, the same way bool is i1 as a value and i8 in memory.
120 // The byte-array storage form for wide split widths is not implemented; a
121 // null return signals that, and op lowerings turn it into errorNYI.
122 if (auto intTy = mlir::dyn_cast<cir::IntType>(type);
123 intTy && intTy.isBitInt()) {
124 if (isSplitStorageBitInt(intTy, dataLayout))
125 return {};
126 return mlir::IntegerType::get(
127 type.getContext(), getBitIntMemoryStorageBits(intTy, dataLayout));
128 }
129
130 return converter.convertType(type);
131}
132
133/// Alignment to use for a memory access whose op carries no explicit alignment.
134/// For _BitInt the storage integer iM's ABI alignment (e.g. i128's 16)
135/// over-aligns the value, so use the CIR _BitInt ABI alignment (e.g. 8).
136static uint64_t getMemoryFallbackAlignment(mlir::Type cirType,
137 mlir::Type llvmMemType,
138 const mlir::DataLayout &dataLayout) {
139 if (auto intTy = mlir::dyn_cast<cir::IntType>(cirType);
140 intTy && intTy.isBitInt())
141 return intTy.getABIAlignment(dataLayout, {});
142 return dataLayout.getTypeABIAlignment(llvmMemType);
143}
144
145static mlir::Value createIntCast(mlir::OpBuilder &bld, mlir::Value src,
146 mlir::IntegerType dstTy,
147 bool isSigned = false) {
148 mlir::Type srcTy = src.getType();
149 assert(mlir::isa<mlir::IntegerType>(srcTy));
150
151 unsigned srcWidth = mlir::cast<mlir::IntegerType>(srcTy).getWidth();
152 unsigned dstWidth = mlir::cast<mlir::IntegerType>(dstTy).getWidth();
153 mlir::Location loc = src.getLoc();
154
155 if (dstWidth > srcWidth && isSigned)
156 return mlir::LLVM::SExtOp::create(bld, loc, dstTy, src);
157 if (dstWidth > srcWidth)
158 return mlir::LLVM::ZExtOp::create(bld, loc, dstTy, src);
159 if (dstWidth < srcWidth)
160 return mlir::LLVM::TruncOp::create(bld, loc, dstTy, src);
161 return mlir::LLVM::BitcastOp::create(bld, loc, dstTy, src);
162}
163
164/// Cast a _BitInt(N) value between its literal width iN and its padded
165/// in-memory storage iM (sign/zero-extend to memory per signedness so the
166/// padding bits are well-defined, truncate back on load; matches classic
167/// CodeGen). Callers must first rule out the unsupported byte-array storage
168/// form (isSplitStorageBitInt / a null convertTypeForMemory result).
169static mlir::Value
170castBitIntMemoryStorage(mlir::ConversionPatternRewriter &rewriter,
171 const mlir::DataLayout &dataLayout, cir::IntType intTy,
172 mlir::Value value, bool toMemory) {
173 unsigned storageBits = getBitIntMemoryStorageBits(intTy, dataLayout);
174 if (storageBits == intTy.getWidth())
175 return value;
176 unsigned dstBits = toMemory ? storageBits : intTy.getWidth();
177 return createIntCast(rewriter, value, rewriter.getIntegerType(dstBits),
178 /*isSigned=*/toMemory && intTy.isSigned());
179}
180
181static mlir::LLVM::Visibility
182lowerCIRVisibilityToLLVMVisibility(cir::VisibilityKind visibilityKind) {
183 switch (visibilityKind) {
184 case cir::VisibilityKind::Default:
185 return ::mlir::LLVM::Visibility::Default;
186 case cir::VisibilityKind::Hidden:
187 return ::mlir::LLVM::Visibility::Hidden;
188 case cir::VisibilityKind::Protected:
189 return ::mlir::LLVM::Visibility::Protected;
190 }
191}
192
193/// Emits the value from memory as expected by its users. Should be called when
194/// the memory represetnation of a CIR type is not equal to its scalar
195/// representation.
196static mlir::Value emitFromMemory(mlir::ConversionPatternRewriter &rewriter,
197 mlir::DataLayout const &dataLayout,
198 cir::LoadOp op, mlir::Value value) {
199
200 // TODO(cir): Handle other types similarly to clang's codegen EmitFromMemory
201 if (auto boolTy = mlir::dyn_cast<cir::BoolType>(op.getType())) {
202 // Create a cast value from specified size in datalayout to i1
203 assert(value.getType().isInteger(dataLayout.getTypeSizeInBits(boolTy)));
204 return createIntCast(rewriter, value, rewriter.getI1Type());
205 }
206
207 // Truncate the padded storage integer back to the _BitInt's literal width.
208 if (auto intTy = mlir::dyn_cast<cir::IntType>(op.getType());
209 intTy && intTy.isBitInt())
210 return castBitIntMemoryStorage(rewriter, dataLayout, intTy, value,
211 /*toMemory=*/false);
212
213 return value;
214}
215
216/// Emits a value to memory with the expected scalar type. Should be called when
217/// the memory represetnation of a CIR type is not equal to its scalar
218/// representation.
219static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter,
220 mlir::DataLayout const &dataLayout,
221 mlir::Type origType, mlir::Value value) {
222
223 // TODO(cir): Handle other types similarly to clang's codegen EmitToMemory
224 if (auto boolTy = mlir::dyn_cast<cir::BoolType>(origType)) {
225 // Create zext of value from i1 to i8
226 mlir::IntegerType memType =
227 rewriter.getIntegerType(dataLayout.getTypeSizeInBits(boolTy));
228 return createIntCast(rewriter, value, memType);
229 }
230
231 // Sign/zero-extend the _BitInt value to its padded storage integer.
232 if (auto intTy = mlir::dyn_cast<cir::IntType>(origType);
233 intTy && intTy.isBitInt())
234 return castBitIntMemoryStorage(rewriter, dataLayout, intTy, value,
235 /*toMemory=*/true);
236
237 return value;
238}
239
240mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
241 using CIR = cir::GlobalLinkageKind;
242 using LLVM = mlir::LLVM::Linkage;
243
244 switch (linkage) {
245 case CIR::AppendingLinkage:
246 return LLVM::Appending;
247 case CIR::AvailableExternallyLinkage:
248 return LLVM::AvailableExternally;
249 case CIR::CommonLinkage:
250 return LLVM::Common;
251 case CIR::ExternalLinkage:
252 return LLVM::External;
253 case CIR::ExternalWeakLinkage:
254 return LLVM::ExternWeak;
255 case CIR::InternalLinkage:
256 return LLVM::Internal;
257 case CIR::LinkOnceAnyLinkage:
258 return LLVM::Linkonce;
259 case CIR::LinkOnceODRLinkage:
260 return LLVM::LinkonceODR;
261 case CIR::PrivateLinkage:
262 return LLVM::Private;
263 case CIR::WeakAnyLinkage:
264 return LLVM::Weak;
265 case CIR::WeakODRLinkage:
266 return LLVM::WeakODR;
267 };
268 llvm_unreachable("Unknown CIR linkage type");
269}
270
271static mlir::LLVM::CConv convertCallingConv(cir::CallingConv callingConv) {
272 using CIR = cir::CallingConv;
273 using LLVM = mlir::LLVM::CConv;
274
275 switch (callingConv) {
276 case CIR::C:
277 return LLVM::C;
278 case CIR::SpirKernel:
279 return LLVM::SPIR_KERNEL;
280 case CIR::SpirFunction:
281 return LLVM::SPIR_FUNC;
282 case CIR::PTXKernel:
283 return LLVM::PTX_Kernel;
284 case CIR::AMDGPUKernel:
285 return LLVM::AMDGPU_KERNEL;
286 }
287 llvm_unreachable("Unknown calling convention");
288}
289
290mlir::LogicalResult CIRToLLVMCopyOpLowering::matchAndRewrite(
291 cir::CopyOp op, OpAdaptor adaptor,
292 mlir::ConversionPatternRewriter &rewriter) const {
293 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
294 const mlir::Value length = mlir::LLVM::ConstantOp::create(
295 rewriter, op.getLoc(), rewriter.getI64Type(),
296 op.getCopySizeInBytes(layout));
298
299 uint64_t dstTypeAlign = dataLayout.getTypeABIAlignment(convertTypeForMemory(
300 *getTypeConverter(), dataLayout, op.getDst().getType().getPointee()));
301 uint64_t srcTypeAlign = dataLayout.getTypeABIAlignment(convertTypeForMemory(
302 *getTypeConverter(), dataLayout, op.getSrc().getType().getPointee()));
303
304 mlir::NamedAttribute dstAlignAttr = rewriter.getNamedAttr(
305 mlir::LLVM::LLVMDialect::getAlignAttrName(),
306 rewriter.getI64IntegerAttr(op.getDstAlignment().value_or(dstTypeAlign)));
307 mlir::NamedAttribute srcAlignAttr = rewriter.getNamedAttr(
308 mlir::LLVM::LLVMDialect::getAlignAttrName(),
309 rewriter.getI64IntegerAttr(op.getSrcAlignment().value_or(srcTypeAlign)));
310 mlir::ArrayAttr argAttrs = rewriter.getArrayAttr({
311 /*dst_attrs=*/rewriter.getDictionaryAttr({dstAlignAttr}),
312 /*src_attrs=*/rewriter.getDictionaryAttr({srcAlignAttr}),
313 });
314
315 rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
316 op, adaptor.getDst(), adaptor.getSrc(), length, op.getIsVolatile(),
317 /*access_groups=*/nullptr, /*alias_scopes=*/nullptr,
318 /*noalias_scopes=*/nullptr, /*tbaa=*/nullptr, /*arg_attrs=*/argAttrs,
319 /*res_attrs=*/nullptr);
320 return mlir::success();
321}
322
323mlir::LogicalResult CIRToLLVMMemCpyOpLowering::matchAndRewrite(
324 cir::MemCpyOp op, OpAdaptor adaptor,
325 mlir::ConversionPatternRewriter &rewriter) const {
326 rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
327 op, adaptor.getDst(), adaptor.getSrc(), adaptor.getLen(),
328 /*isVolatile=*/false);
329 return mlir::success();
330}
331
332mlir::LogicalResult CIRToLLVMMemMoveOpLowering::matchAndRewrite(
333 cir::MemMoveOp op, OpAdaptor adaptor,
334 mlir::ConversionPatternRewriter &rewriter) const {
335 rewriter.replaceOpWithNewOp<mlir::LLVM::MemmoveOp>(
336 op, adaptor.getDst(), adaptor.getSrc(), adaptor.getLen(),
337 /*isVolatile=*/false);
338 return mlir::success();
339}
340
341mlir::LogicalResult CIRToLLVMMemSetOpLowering::matchAndRewrite(
342 cir::MemSetOp op, OpAdaptor adaptor,
343 mlir::ConversionPatternRewriter &rewriter) const {
344
345 auto memset = rewriter.replaceOpWithNewOp<mlir::LLVM::MemsetOp>(
346 op, adaptor.getDst(), adaptor.getVal(), adaptor.getLen(),
347 /*isVolatile=*/false);
348
349 if (op.getAlignmentAttr()) {
350 // Construct a list full of empty attributes.
351 llvm::SmallVector<mlir::Attribute> attrs{memset.getNumOperands(),
352 rewriter.getDictionaryAttr({})};
353 llvm::SmallVector<mlir::NamedAttribute> destAttrs;
354 destAttrs.push_back(
355 {mlir::LLVM::LLVMDialect::getAlignAttrName(), op.getAlignmentAttr()});
356 attrs[memset.odsIndex_dst] = rewriter.getDictionaryAttr(destAttrs);
357
358 auto arrayAttr = rewriter.getArrayAttr(attrs);
359 memset.setArgAttrsAttr(arrayAttr);
360 }
361
362 return mlir::success();
363}
364
365static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter,
366 mlir::Value llvmSrc, mlir::Type llvmDstIntTy,
367 bool isUnsigned, uint64_t cirSrcWidth,
368 uint64_t cirDstIntWidth) {
369 if (cirSrcWidth == cirDstIntWidth)
370 return llvmSrc;
371
372 auto loc = llvmSrc.getLoc();
373 if (cirSrcWidth < cirDstIntWidth) {
374 if (isUnsigned)
375 return mlir::LLVM::ZExtOp::create(rewriter, loc, llvmDstIntTy, llvmSrc);
376 return mlir::LLVM::SExtOp::create(rewriter, loc, llvmDstIntTy, llvmSrc);
377 }
378
379 // Otherwise truncate
380 return mlir::LLVM::TruncOp::create(rewriter, loc, llvmDstIntTy, llvmSrc);
381}
382
384public:
385 CIRAttrToValue(mlir::Operation *parentOp,
386 mlir::ConversionPatternRewriter &rewriter,
387 const mlir::TypeConverter *converter,
388 LLVMBlockAddressInfo *blockInfoAddr = nullptr)
389 : parentOp(parentOp), rewriter(rewriter), converter(converter),
390 blockInfoAddr(blockInfoAddr) {}
391
392#define GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS
393#include "clang/CIR/Dialect/IR/CIRLowering.inc"
394#undef GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS
395
396private:
397 mlir::Operation *parentOp;
398 mlir::ConversionPatternRewriter &rewriter;
399 const mlir::TypeConverter *converter;
400 // Only available when lowering global initializers that may contain block
401 // address attributes. Used to resolve a BlockAddrInfoAttr to its block tag.
402 LLVMBlockAddressInfo *blockInfoAddr;
403};
404
405/// Switches on the type of attribute and calls the appropriate conversion.
406mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp,
407 const mlir::Attribute attr,
408 mlir::ConversionPatternRewriter &rewriter,
409 const mlir::TypeConverter *converter,
410 LLVMBlockAddressInfo *blockInfoAddr) {
411 CIRAttrToValue valueConverter(parentOp, rewriter, converter, blockInfoAddr);
412 mlir::Value value = valueConverter.visit(attr);
413 if (!value)
414 llvm_unreachable("unhandled attribute type");
415 return value;
416}
417
418void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
419 cir::SideEffect sideEffect,
420 mlir::LLVM::MemoryEffectsAttr &memoryEffect,
421 bool &noUnwind, bool &willReturn,
422 bool &noReturn) {
423 using mlir::LLVM::ModRefInfo;
424
425 switch (sideEffect) {
426 case cir::SideEffect::All:
427 memoryEffect = {};
428 noUnwind = isNothrow;
429 willReturn = false;
430 break;
431
432 case cir::SideEffect::Pure:
433 memoryEffect = mlir::LLVM::MemoryEffectsAttr::get(
434 callOp->getContext(), /*other=*/ModRefInfo::Ref,
435 /*argMem=*/ModRefInfo::Ref,
436 /*inaccessibleMem=*/ModRefInfo::Ref,
437 /*errnoMem=*/ModRefInfo::Ref,
438 /*targetMem0=*/ModRefInfo::Ref,
439 /*targetMem1=*/ModRefInfo::Ref);
440 noUnwind = true;
441 willReturn = true;
442 break;
443
444 case cir::SideEffect::Const:
445 memoryEffect = mlir::LLVM::MemoryEffectsAttr::get(
446 callOp->getContext(), /*other=*/ModRefInfo::NoModRef,
447 /*argMem=*/ModRefInfo::NoModRef,
448 /*inaccessibleMem=*/ModRefInfo::NoModRef,
449 /*errnoMem=*/ModRefInfo::NoModRef,
450 /*targetMem0=*/ModRefInfo::NoModRef,
451 /*targetMem1=*/ModRefInfo::NoModRef);
452 noUnwind = true;
453 willReturn = true;
454 break;
455 }
456
457 noReturn = callOp->hasAttr(CIRDialect::getNoReturnAttrName());
458}
459
460static mlir::LLVM::CallIntrinsicOp
461createCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter,
462 mlir::Location loc, const llvm::Twine &intrinsicName,
463 mlir::Type resultTy, mlir::ValueRange operands) {
464 auto intrinsicNameAttr =
465 mlir::StringAttr::get(rewriter.getContext(), intrinsicName);
466 // CallIntrinsicOp has distinct void / single-result create overloads.
467 if (resultTy)
468 return mlir::LLVM::CallIntrinsicOp::create(rewriter, loc, resultTy,
469 intrinsicNameAttr, operands);
470 return mlir::LLVM::CallIntrinsicOp::create(rewriter, loc, intrinsicNameAttr,
471 operands);
472}
473
474static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(
475 mlir::ConversionPatternRewriter &rewriter, mlir::Operation *op,
476 const llvm::Twine &intrinsicName, mlir::Type resultTy,
477 mlir::ValueRange operands) {
478 mlir::LLVM::CallIntrinsicOp callIntrinOp = createCallLLVMIntrinsicOp(
479 rewriter, op->getLoc(), intrinsicName, resultTy, operands);
480 rewriter.replaceOp(op, callIntrinOp.getOperation());
481 return callIntrinOp;
482}
483
484mlir::LogicalResult CIRToLLVMLLVMIntrinsicCallOpLowering::matchAndRewrite(
485 cir::LLVMIntrinsicCallOp op, OpAdaptor adaptor,
486 mlir::ConversionPatternRewriter &rewriter) const {
487 // Result is Optional on the op, so void intrinsics have zero
488 // results; leave llvmResTy null in that case.
489 mlir::Type llvmResTy;
490 if (op->getNumResults() != 0) {
491 llvmResTy = getTypeConverter()->convertType(op->getResultTypes()[0]);
492 if (!llvmResTy)
493 return op.emitError("expected LLVM result type");
494 }
495 StringRef name = op.getIntrinsicName();
496
497 // Some LLVM intrinsics require ElementType attribute to be attached to
498 // the argument of pointer type. That prevents us from generating LLVM IR
499 // because from LLVM dialect, we have LLVM IR like the below which fails
500 // LLVM IR verification.
501 // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr %2)
502 // The expected LLVM IR should be like
503 // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr elementtype(i32) %2)
504 // TODO(cir): MLIR LLVM dialect should handle this part as CIR has no way
505 // to set LLVM IR attribute.
507 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm." + name, llvmResTy,
508 adaptor.getOperands());
509 return mlir::success();
510}
511
512/// BoolAttr visitor.
513mlir::Value CIRAttrToValue::visitCirAttr(cir::BoolAttr boolAttr) {
514 mlir::Location loc = parentOp->getLoc();
515 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
516 mlir::Value boolVal = mlir::LLVM::ConstantOp::create(
517 rewriter, loc, converter->convertType(boolAttr.getType()),
518 boolAttr.getValue());
519 return emitToMemory(rewriter, layout, boolAttr.getType(), boolVal);
520}
521
522/// IntAttr visitor.
523mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
524 mlir::Location loc = parentOp->getLoc();
525 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
526 // Materialize the value at its literal width, then widen to the in-memory
527 // storage type (a no-op except for _BitInt) so aggregate members built here
528 // match the iM struct/array fields produced by convertTypeForMemory.
529 mlir::Value val = mlir::LLVM::ConstantOp::create(
530 rewriter, loc, converter->convertType(intAttr.getType()),
531 intAttr.getValue());
532 return emitToMemory(rewriter, layout, intAttr.getType(), val);
533}
534
535/// FPAttr visitor.
536mlir::Value CIRAttrToValue::visitCirAttr(cir::FPAttr fltAttr) {
537 mlir::Location loc = parentOp->getLoc();
538 return mlir::LLVM::ConstantOp::create(
539 rewriter, loc, converter->convertType(fltAttr.getType()),
540 fltAttr.getValue());
541}
542
543/// ConstComplexAttr visitor.
544mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstComplexAttr complexAttr) {
545 auto complexType = mlir::cast<cir::ComplexType>(complexAttr.getType());
546 mlir::Type complexElemTy = complexType.getElementType();
547 mlir::Type complexElemLLVMTy = converter->convertType(complexElemTy);
548
549 mlir::Attribute components[2];
550 if (const auto intType = mlir::dyn_cast<cir::IntType>(complexElemTy)) {
551 components[0] = rewriter.getIntegerAttr(
552 complexElemLLVMTy,
553 mlir::cast<cir::IntAttr>(complexAttr.getReal()).getValue());
554 components[1] = rewriter.getIntegerAttr(
555 complexElemLLVMTy,
556 mlir::cast<cir::IntAttr>(complexAttr.getImag()).getValue());
557 } else {
558 components[0] = rewriter.getFloatAttr(
559 complexElemLLVMTy,
560 mlir::cast<cir::FPAttr>(complexAttr.getReal()).getValue());
561 components[1] = rewriter.getFloatAttr(
562 complexElemLLVMTy,
563 mlir::cast<cir::FPAttr>(complexAttr.getImag()).getValue());
564 }
565
566 mlir::Location loc = parentOp->getLoc();
567 return mlir::LLVM::ConstantOp::create(
568 rewriter, loc, converter->convertType(complexAttr.getType()),
569 rewriter.getArrayAttr(components));
570}
571
572/// ConstPtrAttr visitor.
573mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstPtrAttr ptrAttr) {
574 mlir::Location loc = parentOp->getLoc();
575 if (ptrAttr.isNullValue()) {
576 return mlir::LLVM::ZeroOp::create(
577 rewriter, loc, converter->convertType(ptrAttr.getType()));
578 }
579 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
580 mlir::Value ptrVal = mlir::LLVM::ConstantOp::create(
581 rewriter, loc,
582 rewriter.getIntegerType(layout.getTypeSizeInBits(ptrAttr.getType())),
583 ptrAttr.getValue().getInt());
584 return mlir::LLVM::IntToPtrOp::create(
585 rewriter, loc, converter->convertType(ptrAttr.getType()), ptrVal);
586}
587
588/// BlockAddrInfoAttr visitor.
589mlir::Value CIRAttrToValue::visitCirAttr(cir::BlockAddrInfoAttr blockAddrInfo) {
590 assert(blockInfoAddr &&
591 "block address lowering requires LLVMBlockAddressInfo");
592 // A block address is lowered to an llvm.blockaddress op that references a
593 // block tag inside the target function. The matching block tag may not have
594 // been emitted yet, in which case the address is recorded as unresolved and
595 // patched up later in resolveBlockAddressOp.
596 mlir::Location loc = parentOp->getLoc();
597 mlir::LLVM::BlockTagOp matchLabel =
598 blockInfoAddr->lookupBlockTag(blockAddrInfo);
599 mlir::LLVM::BlockTagAttr tagAttr =
600 matchLabel ? matchLabel.getTag() : mlir::LLVM::BlockTagAttr{};
601 auto blkAddr = mlir::LLVM::BlockAddressAttr::get(
602 rewriter.getContext(), blockAddrInfo.getFunc(), tagAttr);
603 auto blockAddressOp = mlir::LLVM::BlockAddressOp::create(
604 rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
605 blkAddr);
606 if (!matchLabel)
607 blockInfoAddr->addUnresolvedBlockAddress(blockAddressOp, blockAddrInfo);
608 return blockAddressOp;
609}
610
611// ConstArrayAttr visitor
612mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
613 mlir::Type llvmTy = converter->convertType(attr.getType());
614 mlir::Location loc = parentOp->getLoc();
615 mlir::Value result;
616
617 // When the array can be represented as a single dense constant, emit one
618 // llvm.mlir.constant instead of a chain of llvm.insertvalue ops.
619 if (std::optional<mlir::Attribute> denseAttr =
620 lowerConstArrayAttr(attr, converter))
621 return mlir::LLVM::ConstantOp::create(rewriter, loc, llvmTy, *denseAttr);
622
623 if (attr.hasTrailingZeros()) {
624 mlir::Type arrayTy = attr.getType();
625 result = mlir::LLVM::ZeroOp::create(rewriter, loc,
626 converter->convertType(arrayTy));
627 } else {
628 result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
629 }
630
631 // Iteratively lower each constant element of the array.
632 if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) {
633 for (auto [idx, elt] : llvm::enumerate(arrayAttr)) {
634 mlir::Value init = visit(elt);
635 result =
636 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
637 }
638 } else if (auto strAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) {
639 // TODO(cir): this diverges from traditional lowering. Normally the string
640 // would be a global constant that is memcopied.
641 auto arrayTy = mlir::dyn_cast<cir::ArrayType>(strAttr.getType());
642 assert(arrayTy && "String attribute must have an array type");
643 mlir::Type eltTy = arrayTy.getElementType();
644 for (auto [idx, elt] : llvm::enumerate(strAttr)) {
645 auto init = mlir::LLVM::ConstantOp::create(
646 rewriter, loc, converter->convertType(eltTy), elt);
647 result =
648 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
649 }
650 } else {
651 llvm_unreachable("unexpected ConstArrayAttr elements");
652 }
653
654 return result;
655}
656
657/// ConstRecord visitor.
658mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstRecordAttr constRecord) {
659 mlir::Type llvmTy = converter->convertType(constRecord.getType());
660 mlir::DataLayout dataLayout(parentOp->getParentOfType<mlir::ModuleOp>());
661 llvmTy = adjustGlobalTypeForInit(llvmTy, constRecord, *converter, dataLayout);
662 const mlir::Location loc = parentOp->getLoc();
663 mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
664
665 // Iteratively lower each constant element of the record.
666 for (auto [idx, elt] : llvm::enumerate(constRecord.getMembers())) {
667 mlir::Value init = visit(elt);
668 result =
669 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
670 }
671
672 return result;
673}
674
675/// ConstVectorAttr visitor.
676mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstVectorAttr attr) {
677 const mlir::Type llvmTy = converter->convertType(attr.getType());
678 const mlir::Location loc = parentOp->getLoc();
679
680 SmallVector<mlir::Attribute> mlirValues;
681 for (const mlir::Attribute elementAttr : attr.getElts()) {
682 mlir::Attribute mlirAttr;
683 if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(elementAttr)) {
684 mlirAttr = rewriter.getIntegerAttr(
685 converter->convertType(intAttr.getType()), intAttr.getValue());
686 } else if (auto floatAttr = mlir::dyn_cast<cir::FPAttr>(elementAttr)) {
687 mlirAttr = rewriter.getFloatAttr(
688 converter->convertType(floatAttr.getType()), floatAttr.getValue());
689 } else {
690 llvm_unreachable(
691 "vector constant with an element that is neither an int nor a float");
692 }
693 mlirValues.push_back(mlirAttr);
694 }
695
696 return mlir::LLVM::ConstantOp::create(
697 rewriter, loc, llvmTy,
698 mlir::DenseElementsAttr::get(mlir::cast<mlir::ShapedType>(llvmTy),
699 mlirValues));
700}
701
702// GlobalViewAttr visitor.
703mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
704 auto moduleOp = parentOp->getParentOfType<mlir::ModuleOp>();
705 mlir::DataLayout dataLayout(moduleOp);
706 mlir::Type sourceType;
707 unsigned sourceAddrSpace = 0;
708 llvm::StringRef symName;
709 mlir::Operation *sourceSymbol =
710 mlir::SymbolTable::lookupSymbolIn(moduleOp, globalAttr.getSymbol());
711 if (auto llvmSymbol = dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) {
712 sourceType = llvmSymbol.getType();
713 symName = llvmSymbol.getSymName();
714 sourceAddrSpace = llvmSymbol.getAddrSpace();
715 } else if (auto cirSymbol = dyn_cast<cir::GlobalOp>(sourceSymbol)) {
716 sourceType =
717 convertTypeForMemory(*converter, dataLayout, cirSymbol.getSymType());
718 symName = cirSymbol.getSymName();
719 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
720 cirSymbol.getAddrSpaceAttr()))
721 sourceAddrSpace = targetAS.getValue();
722 } else if (auto llvmFun = dyn_cast<mlir::LLVM::LLVMFuncOp>(sourceSymbol)) {
723 sourceType = llvmFun.getFunctionType();
724 symName = llvmFun.getSymName();
725 } else if (auto fun = dyn_cast<cir::FuncOp>(sourceSymbol)) {
726 sourceType = converter->convertType(fun.getFunctionType());
727 symName = fun.getSymName();
728 } else if (auto alias = dyn_cast<mlir::LLVM::AliasOp>(sourceSymbol)) {
729 sourceType = alias.getType();
730 symName = alias.getSymName();
731 } else {
732 llvm_unreachable("Unexpected GlobalOp type");
733 }
734
735 mlir::Location loc = parentOp->getLoc();
736 mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
737 rewriter, loc,
738 mlir::LLVM::LLVMPointerType::get(rewriter.getContext(), sourceAddrSpace),
739 symName);
740
741 if (globalAttr.getIndices()) {
742 llvm::SmallVector<mlir::LLVM::GEPArg> indices;
743
744 if (mlir::isa<mlir::LLVM::LLVMArrayType, mlir::LLVM::LLVMStructType>(
745 sourceType))
746 indices.push_back(0);
747
748 for (mlir::Attribute idx : globalAttr.getIndices()) {
749 auto intAttr = mlir::cast<mlir::IntegerAttr>(idx);
750 indices.push_back(intAttr.getValue().getSExtValue());
751 }
752 mlir::Type resTy = addrOp.getType();
753 mlir::Type eltTy = converter->convertType(sourceType);
754 addrOp =
755 mlir::LLVM::GEPOp::create(rewriter, loc, resTy, eltTy, addrOp, indices,
756 mlir::LLVM::GEPNoWrapFlags::none);
757 }
758
759 // We can have a global view with an integer type in the case of method
760 // pointers. With the Itanium ABI, the #cir.method attribute is lowered to a
761 // #cir.global_view with a pointer-sized integer representing the address of
762 // the method.
763 if (auto intTy = mlir::dyn_cast<cir::IntType>(globalAttr.getType())) {
764 mlir::Type llvmDstTy = converter->convertType(globalAttr.getType());
765 return mlir::LLVM::PtrToIntOp::create(rewriter, parentOp->getLoc(),
766 llvmDstTy, addrOp);
767 }
768
769 if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(globalAttr.getType())) {
770 auto llvmDstTy = converter->convertType<mlir::LLVM::LLVMPointerType>(ptrTy);
771 unsigned dstAddrSpace = llvmDstTy.getAddressSpace();
772
773 if (sourceAddrSpace != dstAddrSpace)
774 addrOp = mlir::LLVM::AddrSpaceCastOp::create(rewriter, parentOp->getLoc(),
775 llvmDstTy, addrOp);
776
777 mlir::Type llvmEltTy =
778 convertTypeForMemory(*converter, dataLayout, ptrTy.getPointee());
779
780 // No further cast needed if the pointee type already matches.
781 if (llvmEltTy == sourceType)
782 return addrOp;
783
784 // With opaque pointers, the pointer type is already correct (either from
785 // the original AddressOfOp or after an addrspacecast) — skip the
786 // redundant bitcast.
787 if (addrOp.getType() == llvmDstTy)
788 return addrOp;
789
790 return mlir::LLVM::BitcastOp::create(rewriter, parentOp->getLoc(),
791 llvmDstTy, addrOp);
792 }
793
794 if (mlir::isa<cir::VPtrType>(globalAttr.getType()))
795 return addrOp;
796
797 llvm_unreachable("Expecting pointer or integer type for GlobalViewAttr");
798}
799
800// TypeInfoAttr visitor.
801mlir::Value CIRAttrToValue::visitCirAttr(cir::TypeInfoAttr typeInfoAttr) {
802 mlir::Type llvmTy = converter->convertType(typeInfoAttr.getType());
803 mlir::Location loc = parentOp->getLoc();
804 mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
805
806 for (auto [idx, elt] : llvm::enumerate(typeInfoAttr.getData())) {
807 mlir::Value init = visit(elt);
808 result =
809 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
810 }
811
812 return result;
813}
814
815/// UndefAttr visitor.
816mlir::Value CIRAttrToValue::visitCirAttr(cir::UndefAttr undefAttr) {
817 mlir::Location loc = parentOp->getLoc();
818 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
819 return mlir::LLVM::UndefOp::create(
820 rewriter, loc,
821 convertTypeForMemory(*converter, layout, undefAttr.getType()));
822}
823
824/// PoisonAttr visitor.
825mlir::Value CIRAttrToValue::visitCirAttr(cir::PoisonAttr poisonAttr) {
826 mlir::Location loc = parentOp->getLoc();
827 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
828 return mlir::LLVM::PoisonOp::create(
829 rewriter, loc,
830 convertTypeForMemory(*converter, layout, poisonAttr.getType()));
831}
832
833// VTableAttr visitor.
834mlir::Value CIRAttrToValue::visitCirAttr(cir::VTableAttr vtableArr) {
835 mlir::Type llvmTy = converter->convertType(vtableArr.getType());
836 mlir::Location loc = parentOp->getLoc();
837 mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
838
839 for (auto [idx, elt] : llvm::enumerate(vtableArr.getData())) {
840 mlir::Value init = visit(elt);
841 result =
842 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
843 }
844
845 return result;
846}
847
848/// ZeroAttr visitor.
849mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) {
850 mlir::Location loc = parentOp->getLoc();
851 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
852 return mlir::LLVM::ZeroOp::create(
853 rewriter, loc, convertTypeForMemory(*converter, layout, attr.getType()));
854}
855
856// This class handles rewriting initializer attributes for types that do not
857// require region initialization.
859public:
860 GlobalInitAttrRewriter(mlir::Type type,
861 mlir::ConversionPatternRewriter &rewriter)
862 : llvmType(type), rewriter(rewriter) {}
863
864 mlir::Attribute visit(mlir::Attribute attr) {
865 return llvm::TypeSwitch<mlir::Attribute, mlir::Attribute>(attr)
866 .Case<cir::IntAttr, cir::FPAttr, cir::BoolAttr>(
867 [&](auto attrT) { return visitCirAttr(attrT); })
868 .Default([&](auto attrT) { return mlir::Attribute(); });
869 }
870
871 mlir::Attribute visitCirAttr(cir::IntAttr attr) {
872 // A _BitInt(N) global stores its value in a padded integer iM; sign/zero-
873 // extend the APInt to that width (a no-op for plain integers, whose value
874 // width already matches llvmType) so the IntegerAttr is well-typed.
875 llvm::APInt val = attr.getValue();
876 auto destTy = mlir::cast<mlir::IntegerType>(llvmType);
877 if (val.getBitWidth() != destTy.getWidth()) {
878 auto cirIntTy = mlir::cast<cir::IntType>(attr.getType());
879 val = cirIntTy.isSigned() ? val.sext(destTy.getWidth())
880 : val.zext(destTy.getWidth());
881 }
882 return rewriter.getIntegerAttr(llvmType, val);
883 }
884
885 mlir::Attribute visitCirAttr(cir::FPAttr attr) {
886 return rewriter.getFloatAttr(llvmType, attr.getValue());
887 }
888
889 mlir::Attribute visitCirAttr(cir::BoolAttr attr) {
890 return rewriter.getBoolAttr(attr.getValue());
891 }
892
893private:
894 mlir::Type llvmType;
895 mlir::ConversionPatternRewriter &rewriter;
896};
897
898// This pass requires the CIR to be in a "flat" state. All blocks in each
899// function must belong to the parent region. Once scopes and control flow
900// are implemented in CIR, a pass will be run before this one to flatten
901// the CIR and get it into the state that this pass requires.
903 : public mlir::PassWrapper<ConvertCIRToLLVMPass,
904 mlir::OperationPass<mlir::ModuleOp>> {
905 void getDependentDialects(mlir::DialectRegistry &registry) const override {
906 registry.insert<mlir::BuiltinDialect, mlir::DLTIDialect,
907 mlir::LLVM::LLVMDialect, mlir::func::FuncDialect>();
908 }
909 void runOnOperation() final;
910
911 void processCIRAttrs(mlir::ModuleOp module);
912
913 void resolveBlockAddressOp(LLVMBlockAddressInfo &blockInfoAddr);
914
915 /// Collect (symbol_name, annotations, loc) from cir.func and cir.global ops
916 /// before the conversion runs (the annotations attribute is dropped during
917 /// FuncOp/GlobalOp lowering).
918 void collectGlobalAnnotations(mlir::ModuleOp module);
919
920 /// Emit @llvm.global.annotations and supporting string/args constants from
921 /// the previously-collected annotations. Mirrors what OGCG produces.
922 void buildGlobalAnnotationsVar(mlir::ModuleOp module);
923
924 StringRef getDescription() const override {
925 return "Convert the prepared CIR dialect module to LLVM dialect";
926 }
927
928 StringRef getArgument() const override { return "cir-flat-to-llvm"; }
929
930private:
931 /// One annotation entry collected pre-conversion.
932 struct CollectedAnnotation {
933 mlir::StringAttr symName;
934 cir::AnnotationAttr annotation;
935 mlir::Location loc;
936 CollectedAnnotation(mlir::StringAttr symName,
937 cir::AnnotationAttr annotation, mlir::Location loc)
938 : symName(symName), annotation(annotation), loc(loc) {}
939 };
940 llvm::SmallVector<CollectedAnnotation> collectedAnnotations;
941};
942
943mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite(
944 cir::IsFPClassOp op, OpAdaptor adaptor,
945 mlir::ConversionPatternRewriter &rewriter) const {
946 mlir::Value src = adaptor.getSrc();
947 cir::FPClassTest flags = adaptor.getFlags();
948 mlir::IntegerType retTy = rewriter.getI1Type();
949
950 rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>(
951 op, retTy, src, static_cast<uint32_t>(flags));
952 return mlir::success();
953}
954
955mlir::LogicalResult CIRToLLVMSignBitOpLowering::matchAndRewrite(
956 cir::SignBitOp op, OpAdaptor adaptor,
957 mlir::ConversionPatternRewriter &rewriter) const {
959
960 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
961 int width = layout.getTypeSizeInBits(op.getInput().getType());
962 if (auto longDoubleType =
963 mlir::dyn_cast<cir::LongDoubleType>(op.getInput().getType())) {
964 if (mlir::isa<cir::FP80Type>(longDoubleType.getUnderlying())) {
965 // If the underlying type of LongDouble is FP80Type,
966 // DataLayout::getTypeSizeInBits returns 128.
967 // See https://github.com/llvm/clangir/issues/1057.
968 // Set the width to 80 manually.
969 width = 80;
970 }
971 }
972 mlir::Type intTy = mlir::IntegerType::get(rewriter.getContext(), width);
973 auto bitcast = mlir::LLVM::BitcastOp::create(rewriter, op->getLoc(), intTy,
974 adaptor.getInput());
975
976 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op->getLoc(), intTy, 0);
977 auto cmpResult = mlir::LLVM::ICmpOp::create(rewriter, op.getLoc(),
978 mlir::LLVM::ICmpPredicate::slt,
979 bitcast.getResult(), zero);
980 rewriter.replaceOp(op, cmpResult);
981 return mlir::success();
982}
983
984mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite(
985 cir::AssumeOp op, OpAdaptor adaptor,
986 mlir::ConversionPatternRewriter &rewriter) const {
987 mlir::Value cond = adaptor.getPredicate();
988 if (op.getBundleKind() == cir::AssumeBundleKind::None) {
989 rewriter.replaceOpWithNewOp<mlir::LLVM::AssumeOp>(op, cond);
990 return mlir::success();
991 }
992
993 llvm::StringRef tag = cir::stringifyAssumeBundleKind(op.getBundleKind());
994 rewriter.replaceOpWithNewOp<mlir::LLVM::AssumeOp>(op, cond, tag,
995 adaptor.getBundleArgs());
996 return mlir::success();
997}
998
999static mlir::LLVM::AtomicOrdering
1000getLLVMMemOrder(std::optional<cir::MemOrder> memorder) {
1001 if (!memorder)
1002 return mlir::LLVM::AtomicOrdering::not_atomic;
1003 switch (*memorder) {
1004 case cir::MemOrder::Relaxed:
1005 return mlir::LLVM::AtomicOrdering::monotonic;
1006 case cir::MemOrder::Consume:
1007 case cir::MemOrder::Acquire:
1008 return mlir::LLVM::AtomicOrdering::acquire;
1009 case cir::MemOrder::Release:
1010 return mlir::LLVM::AtomicOrdering::release;
1011 case cir::MemOrder::AcquireRelease:
1012 return mlir::LLVM::AtomicOrdering::acq_rel;
1013 case cir::MemOrder::SequentiallyConsistent:
1014 return mlir::LLVM::AtomicOrdering::seq_cst;
1015 }
1016 llvm_unreachable("unknown memory order");
1017}
1018
1019static llvm::StringRef getLLVMSyncScope(cir::SyncScopeKind syncScope) {
1020 switch (syncScope) {
1021 case cir::SyncScopeKind::SingleThread:
1022 return "singlethread";
1023 case cir::SyncScopeKind::Workgroup:
1024 return "block";
1025 default:
1026 return "";
1027 }
1028}
1029
1030static std::optional<llvm::StringRef>
1031getLLVMSyncScope(std::optional<cir::SyncScopeKind> syncScope) {
1032 if (syncScope.has_value())
1033 return getLLVMSyncScope(*syncScope);
1034 return std::nullopt;
1035}
1036
1037mlir::LogicalResult CIRToLLVMAtomicCmpXchgOpLowering::matchAndRewrite(
1038 cir::AtomicCmpXchgOp op, OpAdaptor adaptor,
1039 mlir::ConversionPatternRewriter &rewriter) const {
1040 mlir::Value expected = adaptor.getExpected();
1041 mlir::Value desired = adaptor.getDesired();
1042
1043 auto cmpxchg = mlir::LLVM::AtomicCmpXchgOp::create(
1044 rewriter, op.getLoc(), adaptor.getPtr(), expected, desired,
1045 getLLVMMemOrder(adaptor.getSuccOrder()),
1046 getLLVMMemOrder(adaptor.getFailOrder()),
1047 getLLVMSyncScope(op.getSyncScope()));
1048
1049 cmpxchg.setAlignment(adaptor.getAlignment());
1050 cmpxchg.setWeak(adaptor.getWeak());
1051 cmpxchg.setVolatile_(adaptor.getIsVolatile());
1052
1053 // Check result and apply stores accordingly.
1054 auto old = mlir::LLVM::ExtractValueOp::create(rewriter, op.getLoc(),
1055 cmpxchg.getResult(), 0);
1056 auto cmp = mlir::LLVM::ExtractValueOp::create(rewriter, op.getLoc(),
1057 cmpxchg.getResult(), 1);
1058
1059 rewriter.replaceOp(op, {old, cmp});
1060 return mlir::success();
1061}
1062
1063mlir::LogicalResult CIRToLLVMAtomicXchgOpLowering::matchAndRewrite(
1064 cir::AtomicXchgOp op, OpAdaptor adaptor,
1065 mlir::ConversionPatternRewriter &rewriter) const {
1067 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(adaptor.getMemOrder());
1068 llvm::StringRef llvmSyncScope = getLLVMSyncScope(adaptor.getSyncScope());
1069 rewriter.replaceOpWithNewOp<mlir::LLVM::AtomicRMWOp>(
1070 op, mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(), adaptor.getVal(),
1071 llvmOrder, llvmSyncScope);
1072 return mlir::success();
1073}
1074
1075mlir::LogicalResult CIRToLLVMAtomicTestAndSetOpLowering::matchAndRewrite(
1076 cir::AtomicTestAndSetOp op, OpAdaptor adaptor,
1077 mlir::ConversionPatternRewriter &rewriter) const {
1079
1080 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
1081
1082 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
1083 rewriter.getI8Type(), 1);
1084 auto rmw = mlir::LLVM::AtomicRMWOp::create(
1085 rewriter, op.getLoc(), mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(),
1086 one, llvmOrder, /*syncscope=*/llvm::StringRef(),
1087 adaptor.getAlignment().value_or(0), op.getIsVolatile());
1088
1089 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
1090 rewriter.getI8Type(), 0);
1091 auto cmp = mlir::LLVM::ICmpOp::create(
1092 rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, rmw, zero);
1093
1094 rewriter.replaceOp(op, cmp);
1095 return mlir::success();
1096}
1097
1098mlir::LogicalResult CIRToLLVMAtomicClearOpLowering::matchAndRewrite(
1099 cir::AtomicClearOp op, OpAdaptor adaptor,
1100 mlir::ConversionPatternRewriter &rewriter) const {
1102
1103 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
1104 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
1105 rewriter.getI8Type(), 0);
1106 auto store = mlir::LLVM::StoreOp::create(
1107 rewriter, op.getLoc(), zero, adaptor.getPtr(),
1108 adaptor.getAlignment().value_or(0), op.getIsVolatile(),
1109 /*isNonTemporal=*/false, /*isInvariantGroup=*/false, llvmOrder);
1110
1111 rewriter.replaceOp(op, store);
1112 return mlir::success();
1113}
1114
1115mlir::LogicalResult CIRToLLVMAtomicFenceOpLowering::matchAndRewrite(
1116 cir::AtomicFenceOp op, OpAdaptor adaptor,
1117 mlir::ConversionPatternRewriter &rewriter) const {
1118 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(adaptor.getOrdering());
1119
1120 auto fence = mlir::LLVM::FenceOp::create(rewriter, op.getLoc(), llvmOrder);
1121 fence.setSyncscope(getLLVMSyncScope(adaptor.getSyncscope()));
1122
1123 rewriter.replaceOp(op, fence);
1124
1125 return mlir::success();
1126}
1127
1128static mlir::LLVM::AtomicBinOp
1129getLLVMAtomicBinOp(cir::AtomicFetchKind k, bool isInt, bool isSignedInt) {
1130 switch (k) {
1131 case cir::AtomicFetchKind::Add:
1132 return isInt ? mlir::LLVM::AtomicBinOp::add : mlir::LLVM::AtomicBinOp::fadd;
1133 case cir::AtomicFetchKind::Sub:
1134 return isInt ? mlir::LLVM::AtomicBinOp::sub : mlir::LLVM::AtomicBinOp::fsub;
1135 case cir::AtomicFetchKind::And:
1136 return mlir::LLVM::AtomicBinOp::_and;
1137 case cir::AtomicFetchKind::Xor:
1138 return mlir::LLVM::AtomicBinOp::_xor;
1139 case cir::AtomicFetchKind::Or:
1140 return mlir::LLVM::AtomicBinOp::_or;
1141 case cir::AtomicFetchKind::Nand:
1142 return mlir::LLVM::AtomicBinOp::nand;
1143 case cir::AtomicFetchKind::Max: {
1144 if (!isInt)
1145 return mlir::LLVM::AtomicBinOp::fmax;
1146 return isSignedInt ? mlir::LLVM::AtomicBinOp::max
1147 : mlir::LLVM::AtomicBinOp::umax;
1148 }
1149 case cir::AtomicFetchKind::Min: {
1150 if (!isInt)
1151 return mlir::LLVM::AtomicBinOp::fmin;
1152 return isSignedInt ? mlir::LLVM::AtomicBinOp::min
1153 : mlir::LLVM::AtomicBinOp::umin;
1154 }
1155 case cir::AtomicFetchKind::UIncWrap:
1156 return mlir::LLVM::AtomicBinOp::uinc_wrap;
1157 case cir::AtomicFetchKind::UDecWrap:
1158 return mlir::LLVM::AtomicBinOp::udec_wrap;
1159 case cir::AtomicFetchKind::Maximum:
1160 return mlir::LLVM::AtomicBinOp::fmaximum;
1161 case cir::AtomicFetchKind::Minimum:
1162 return mlir::LLVM::AtomicBinOp::fminimum;
1163 case cir::AtomicFetchKind::MaximumNum:
1164 return mlir::LLVM::AtomicBinOp::fmaximumnum;
1165 case cir::AtomicFetchKind::MinimumNum:
1166 return mlir::LLVM::AtomicBinOp::fminimumnum;
1167 }
1168 llvm_unreachable("Unknown atomic fetch opcode");
1169}
1170
1171static llvm::StringLiteral getLLVMBinopForPostAtomic(cir::AtomicFetchKind k,
1172 bool isInt) {
1173 switch (k) {
1174 case cir::AtomicFetchKind::Add:
1175 return isInt ? mlir::LLVM::AddOp::getOperationName()
1176 : mlir::LLVM::FAddOp::getOperationName();
1177 case cir::AtomicFetchKind::Sub:
1178 return isInt ? mlir::LLVM::SubOp::getOperationName()
1179 : mlir::LLVM::FSubOp::getOperationName();
1180 case cir::AtomicFetchKind::And:
1181 return mlir::LLVM::AndOp::getOperationName();
1182 case cir::AtomicFetchKind::Xor:
1183 return mlir::LLVM::XOrOp::getOperationName();
1184 case cir::AtomicFetchKind::Or:
1185 return mlir::LLVM::OrOp::getOperationName();
1186 case cir::AtomicFetchKind::Nand:
1187 // There's no nand binop in LLVM, this is later fixed with a not.
1188 return mlir::LLVM::AndOp::getOperationName();
1189 case cir::AtomicFetchKind::Max:
1190 case cir::AtomicFetchKind::Min:
1191 llvm_unreachable("handled in buildMinMaxPostOp");
1192 case cir::AtomicFetchKind::UIncWrap:
1193 case cir::AtomicFetchKind::UDecWrap:
1194 case cir::AtomicFetchKind::Maximum:
1195 case cir::AtomicFetchKind::Minimum:
1196 case cir::AtomicFetchKind::MaximumNum:
1197 case cir::AtomicFetchKind::MinimumNum:
1198 llvm_unreachable("uinc_wrap, udec_wrap, maximum, minimum, maximum_num, and "
1199 "minimum_num are always fetch_first");
1200 }
1201 llvm_unreachable("Unknown atomic fetch opcode");
1202}
1203
1204mlir::Value CIRToLLVMAtomicFetchOpLowering::buildPostOp(
1205 cir::AtomicFetchOp op, OpAdaptor adaptor,
1206 mlir::ConversionPatternRewriter &rewriter, mlir::Value rmwVal,
1207 bool isInt) const {
1208 SmallVector<mlir::Value> atomicOperands = {rmwVal, adaptor.getVal()};
1209 SmallVector<mlir::Type> atomicResTys = {rmwVal.getType()};
1210 return rewriter
1211 .create(op.getLoc(),
1212 rewriter.getStringAttr(
1213 getLLVMBinopForPostAtomic(op.getBinop(), isInt)),
1214 atomicOperands, atomicResTys, {})
1215 ->getResult(0);
1216}
1217
1218mlir::Value CIRToLLVMAtomicFetchOpLowering::buildMinMaxPostOp(
1219 cir::AtomicFetchOp op, OpAdaptor adaptor,
1220 mlir::ConversionPatternRewriter &rewriter, mlir::Value rmwVal, bool isInt,
1221 bool isSigned) const {
1222 mlir::Location loc = op.getLoc();
1223
1224 if (!isInt) {
1225 if (op.getBinop() == cir::AtomicFetchKind::Max)
1226 return mlir::LLVM::MaxNumOp::create(rewriter, loc, rmwVal,
1227 adaptor.getVal());
1228 return mlir::LLVM::MinNumOp::create(rewriter, loc, rmwVal,
1229 adaptor.getVal());
1230 }
1231
1232 mlir::LLVM::ICmpPredicate pred;
1233 if (op.getBinop() == cir::AtomicFetchKind::Max) {
1234 pred = isSigned ? mlir::LLVM::ICmpPredicate::sgt
1235 : mlir::LLVM::ICmpPredicate::ugt;
1236 } else { // Min
1237 pred = isSigned ? mlir::LLVM::ICmpPredicate::slt
1238 : mlir::LLVM::ICmpPredicate::ult;
1239 }
1240 mlir::Value cmp = mlir::LLVM::ICmpOp::create(
1241 rewriter, loc,
1242 mlir::LLVM::ICmpPredicateAttr::get(rewriter.getContext(), pred), rmwVal,
1243 adaptor.getVal());
1244 return mlir::LLVM::SelectOp::create(rewriter, loc, cmp, rmwVal,
1245 adaptor.getVal());
1246}
1247
1248mlir::LogicalResult CIRToLLVMAtomicFetchOpLowering::matchAndRewrite(
1249 cir::AtomicFetchOp op, OpAdaptor adaptor,
1250 mlir::ConversionPatternRewriter &rewriter) const {
1251 bool isInt = false;
1252 bool isSignedInt = false;
1253 if (auto intTy = mlir::dyn_cast<cir::IntType>(op.getVal().getType())) {
1254 isInt = true;
1255 isSignedInt = intTy.isSigned();
1256 } else if (mlir::isa<cir::SingleType, cir::DoubleType>(
1257 op.getVal().getType())) {
1258 isInt = false;
1259 } else {
1260 return op.emitError() << "Unsupported type: " << op.getVal().getType();
1261 }
1262
1263 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
1264 llvm::StringRef llvmSyncScope = getLLVMSyncScope(op.getSyncScope());
1265 mlir::LLVM::AtomicBinOp llvmBinOp =
1266 getLLVMAtomicBinOp(op.getBinop(), isInt, isSignedInt);
1267 auto rmwVal = mlir::LLVM::AtomicRMWOp::create(
1268 rewriter, op.getLoc(), llvmBinOp, adaptor.getPtr(), adaptor.getVal(),
1269 llvmOrder, llvmSyncScope);
1270
1271 mlir::Value result = rmwVal.getResult();
1272 if (!op.getFetchFirst()) {
1273 if (op.getBinop() == cir::AtomicFetchKind::Max ||
1274 op.getBinop() == cir::AtomicFetchKind::Min)
1275 result = buildMinMaxPostOp(op, adaptor, rewriter, rmwVal.getRes(), isInt,
1276 isSignedInt);
1277 else
1278 result = buildPostOp(op, adaptor, rewriter, rmwVal.getRes(), isInt);
1279
1280 // Compensate lack of nand binop in LLVM IR.
1281 if (op.getBinop() == cir::AtomicFetchKind::Nand) {
1282 auto negOne = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
1283 result.getType(), -1);
1284 result = mlir::LLVM::XOrOp::create(rewriter, op.getLoc(), result, negOne);
1285 }
1286 }
1287
1288 rewriter.replaceOp(op, result);
1289 return mlir::success();
1290}
1291
1292mlir::LogicalResult CIRToLLVMBitClrsbOpLowering::matchAndRewrite(
1293 cir::BitClrsbOp op, OpAdaptor adaptor,
1294 mlir::ConversionPatternRewriter &rewriter) const {
1295 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
1296 adaptor.getInput().getType(), 0);
1297 auto isNeg = mlir::LLVM::ICmpOp::create(
1298 rewriter, op.getLoc(),
1299 mlir::LLVM::ICmpPredicateAttr::get(rewriter.getContext(),
1300 mlir::LLVM::ICmpPredicate::slt),
1301 adaptor.getInput(), zero);
1302
1303 auto negOne = mlir::LLVM::ConstantOp::create(
1304 rewriter, op.getLoc(), adaptor.getInput().getType(), -1);
1305 auto flipped = mlir::LLVM::XOrOp::create(rewriter, op.getLoc(),
1306 adaptor.getInput(), negOne);
1307
1308 auto select = mlir::LLVM::SelectOp::create(rewriter, op.getLoc(), isNeg,
1309 flipped, adaptor.getInput());
1310
1311 auto resTy = getTypeConverter()->convertType(op.getType());
1312 auto clz = mlir::LLVM::CountLeadingZerosOp::create(
1313 rewriter, op.getLoc(), resTy, select, /*is_zero_poison=*/false);
1314
1315 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 1);
1316 auto res = mlir::LLVM::SubOp::create(rewriter, op.getLoc(), clz, one,
1317 mlir::LLVM::IntegerOverflowFlags::nuw);
1318 rewriter.replaceOp(op, res);
1319
1320 return mlir::LogicalResult::success();
1321}
1322
1323mlir::LogicalResult CIRToLLVMBitClzOpLowering::matchAndRewrite(
1324 cir::BitClzOp op, OpAdaptor adaptor,
1325 mlir::ConversionPatternRewriter &rewriter) const {
1326 auto resTy = getTypeConverter()->convertType(op.getType());
1327 auto llvmOp = mlir::LLVM::CountLeadingZerosOp::create(
1328 rewriter, op.getLoc(), resTy, adaptor.getInput(), op.getPoisonZero());
1329 rewriter.replaceOp(op, llvmOp);
1330 return mlir::LogicalResult::success();
1331}
1332
1333mlir::LogicalResult CIRToLLVMBitCtzOpLowering::matchAndRewrite(
1334 cir::BitCtzOp op, OpAdaptor adaptor,
1335 mlir::ConversionPatternRewriter &rewriter) const {
1336 auto resTy = getTypeConverter()->convertType(op.getType());
1337 auto llvmOp = mlir::LLVM::CountTrailingZerosOp::create(
1338 rewriter, op.getLoc(), resTy, adaptor.getInput(), op.getPoisonZero());
1339 rewriter.replaceOp(op, llvmOp);
1340 return mlir::LogicalResult::success();
1341}
1342
1343mlir::LogicalResult CIRToLLVMBitFfsOpLowering::matchAndRewrite(
1344 cir::BitFfsOp op, OpAdaptor adaptor,
1345 mlir::ConversionPatternRewriter &rewriter) const {
1346 auto resTy = getTypeConverter()->convertType(op.getType());
1347 auto ctz = mlir::LLVM::CountTrailingZerosOp::create(rewriter, op.getLoc(),
1348 resTy, adaptor.getInput(),
1349 /*is_zero_poison=*/true);
1350
1351 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 1);
1352 auto ctzAddOne = mlir::LLVM::AddOp::create(rewriter, op.getLoc(), ctz, one);
1353
1354 auto zeroInputTy = mlir::LLVM::ConstantOp::create(
1355 rewriter, op.getLoc(), adaptor.getInput().getType(), 0);
1356 auto isZero = mlir::LLVM::ICmpOp::create(
1357 rewriter, op.getLoc(),
1358 mlir::LLVM::ICmpPredicateAttr::get(rewriter.getContext(),
1359 mlir::LLVM::ICmpPredicate::eq),
1360 adaptor.getInput(), zeroInputTy);
1361
1362 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 0);
1363 auto res = mlir::LLVM::SelectOp::create(rewriter, op.getLoc(), isZero, zero,
1364 ctzAddOne);
1365 rewriter.replaceOp(op, res);
1366
1367 return mlir::LogicalResult::success();
1368}
1369
1370mlir::LogicalResult CIRToLLVMBitParityOpLowering::matchAndRewrite(
1371 cir::BitParityOp op, OpAdaptor adaptor,
1372 mlir::ConversionPatternRewriter &rewriter) const {
1373 auto resTy = getTypeConverter()->convertType(op.getType());
1374 auto popcnt = mlir::LLVM::CtPopOp::create(rewriter, op.getLoc(), resTy,
1375 adaptor.getInput());
1376
1377 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 1);
1378 auto popcntMod2 =
1379 mlir::LLVM::AndOp::create(rewriter, op.getLoc(), popcnt, one);
1380 rewriter.replaceOp(op, popcntMod2);
1381
1382 return mlir::LogicalResult::success();
1383}
1384
1385mlir::LogicalResult CIRToLLVMBitPopcountOpLowering::matchAndRewrite(
1386 cir::BitPopcountOp op, OpAdaptor adaptor,
1387 mlir::ConversionPatternRewriter &rewriter) const {
1388 auto resTy = getTypeConverter()->convertType(op.getType());
1389 auto llvmOp = mlir::LLVM::CtPopOp::create(rewriter, op.getLoc(), resTy,
1390 adaptor.getInput());
1391 rewriter.replaceOp(op, llvmOp);
1392 return mlir::LogicalResult::success();
1393}
1394
1395mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
1396 cir::BrCondOp brOp, OpAdaptor adaptor,
1397 mlir::ConversionPatternRewriter &rewriter) const {
1398 // When ZExtOp is implemented, we'll need to check if the condition is a
1399 // ZExtOp and if so, delete it if it has a single use.
1401
1402 mlir::Value i1Condition = adaptor.getCond();
1403
1404 rewriter.replaceOpWithNewOp<mlir::LLVM::CondBrOp>(
1405 brOp, i1Condition, brOp.getDestTrue(), adaptor.getDestOperandsTrue(),
1406 brOp.getDestFalse(), adaptor.getDestOperandsFalse());
1407
1408 return mlir::success();
1409}
1410
1411mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const {
1412 return getTypeConverter()->convertType(ty);
1413}
1414
1415mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite(
1416 cir::CastOp castOp, OpAdaptor adaptor,
1417 mlir::ConversionPatternRewriter &rewriter) const {
1418 // For arithmetic conversions, LLVM IR uses the same instruction to convert
1419 // both individual scalars and entire vectors. This lowering pass handles
1420 // both situations.
1421
1422 switch (castOp.getKind()) {
1423 case cir::CastKind::array_to_ptrdecay: {
1424 const auto ptrTy = mlir::cast<cir::PointerType>(castOp.getType());
1425 mlir::Value sourceValue = adaptor.getSrc();
1426 mlir::Type targetType = convertTy(ptrTy);
1427 mlir::Type elementTy = convertTypeForMemory(*getTypeConverter(), dataLayout,
1428 ptrTy.getPointee());
1429 llvm::SmallVector<mlir::LLVM::GEPArg> offset{0};
1430 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1431 castOp, targetType, elementTy, sourceValue, offset);
1432 break;
1433 }
1434 case cir::CastKind::int_to_bool: {
1435 mlir::Value llvmSrcVal = adaptor.getSrc();
1436 // getZeroAttr yields a splat for vector source types so this also
1437 // handles element-wise int-to-bool conversions (e.g. an ext_vector
1438 // __builtin_convertvector to bool).
1439 mlir::Value zeroInt = mlir::LLVM::ConstantOp::create(
1440 rewriter, castOp.getLoc(), llvmSrcVal.getType(),
1441 rewriter.getZeroAttr(llvmSrcVal.getType()));
1442 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1443 castOp, mlir::LLVM::ICmpPredicate::ne, llvmSrcVal, zeroInt);
1444 break;
1445 }
1446 case cir::CastKind::integral: {
1447 mlir::Type srcType = castOp.getSrc().getType();
1448 mlir::Type dstType = castOp.getType();
1449 mlir::Value llvmSrcVal = adaptor.getSrc();
1450 mlir::Type llvmDstType = getTypeConverter()->convertType(dstType);
1451 cir::IntType srcIntType =
1452 mlir::cast<cir::IntType>(elementTypeIfVector(srcType));
1453 cir::IntType dstIntType =
1454 mlir::cast<cir::IntType>(elementTypeIfVector(dstType));
1455 rewriter.replaceOp(castOp, getLLVMIntCast(rewriter, llvmSrcVal, llvmDstType,
1456 srcIntType.isUnsigned(),
1457 srcIntType.getWidth(),
1458 dstIntType.getWidth()));
1459 break;
1460 }
1461 case cir::CastKind::floating: {
1462 mlir::Value llvmSrcVal = adaptor.getSrc();
1463 mlir::Type llvmDstTy = getTypeConverter()->convertType(castOp.getType());
1464
1465 mlir::Type srcTy = elementTypeIfVector(castOp.getSrc().getType());
1466 mlir::Type dstTy = elementTypeIfVector(castOp.getType());
1467
1468 if (!mlir::isa<cir::FPTypeInterface>(dstTy) ||
1469 !mlir::isa<cir::FPTypeInterface>(srcTy))
1470 return castOp.emitError() << "NYI cast from " << srcTy << " to " << dstTy;
1471
1472 auto getFloatWidth = [](mlir::Type ty) -> unsigned {
1473 return mlir::cast<cir::FPTypeInterface>(ty).getWidth();
1474 };
1475
1476 if (getFloatWidth(srcTy) > getFloatWidth(dstTy))
1477 rewriter.replaceOpWithNewOp<mlir::LLVM::FPTruncOp>(castOp, llvmDstTy,
1478 llvmSrcVal);
1479 else
1480 rewriter.replaceOpWithNewOp<mlir::LLVM::FPExtOp>(castOp, llvmDstTy,
1481 llvmSrcVal);
1482 return mlir::success();
1483 }
1484 case cir::CastKind::int_to_ptr: {
1485 auto dstTy = mlir::cast<cir::PointerType>(castOp.getType());
1486 mlir::Value llvmSrcVal = adaptor.getSrc();
1487 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1488 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(castOp, llvmDstTy,
1489 llvmSrcVal);
1490 return mlir::success();
1491 }
1492 case cir::CastKind::ptr_to_int: {
1493 auto dstTy = mlir::cast<cir::IntType>(castOp.getType());
1494 mlir::Value llvmSrcVal = adaptor.getSrc();
1495 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1496 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(castOp, llvmDstTy,
1497 llvmSrcVal);
1498 return mlir::success();
1499 }
1500 case cir::CastKind::float_to_bool: {
1501 mlir::Value llvmSrcVal = adaptor.getSrc();
1502 auto kind = mlir::LLVM::FCmpPredicate::une;
1503
1504 // Check if float is not equal to zero. getZeroAttr yields a splat
1505 // for vector source types so this also handles element-wise
1506 // float-to-bool conversions.
1507 auto zeroFloat = mlir::LLVM::ConstantOp::create(
1508 rewriter, castOp.getLoc(), llvmSrcVal.getType(),
1509 rewriter.getZeroAttr(llvmSrcVal.getType()));
1510
1511 // Extend comparison result to either bool (C++) or int (C).
1512 rewriter.replaceOpWithNewOp<mlir::LLVM::FCmpOp>(castOp, kind, llvmSrcVal,
1513 zeroFloat);
1514
1515 return mlir::success();
1516 }
1517 case cir::CastKind::bool_to_int: {
1518 mlir::Type dstTy = castOp.getType();
1519 mlir::Value llvmSrcVal = adaptor.getSrc();
1520 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1521 // Compare element widths so this also handles vector bool -> int casts.
1522 auto srcElemTy = mlir::cast<mlir::IntegerType>(
1523 elementTypeIfVector(llvmSrcVal.getType()));
1524 auto dstElemTy = mlir::cast<cir::IntType>(elementTypeIfVector(dstTy));
1525
1526 if (srcElemTy.getWidth() == dstElemTy.getWidth())
1527 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(castOp, llvmDstTy,
1528 llvmSrcVal);
1529 else
1530 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(castOp, llvmDstTy,
1531 llvmSrcVal);
1532 return mlir::success();
1533 }
1534 case cir::CastKind::bool_to_float: {
1535 mlir::Type dstTy = castOp.getType();
1536 mlir::Value llvmSrcVal = adaptor.getSrc();
1537 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1538 rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy,
1539 llvmSrcVal);
1540 return mlir::success();
1541 }
1542 case cir::CastKind::int_to_float: {
1543 mlir::Type dstTy = castOp.getType();
1544 mlir::Value llvmSrcVal = adaptor.getSrc();
1545 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1546 if (mlir::cast<cir::IntType>(elementTypeIfVector(castOp.getSrc().getType()))
1547 .isSigned())
1548 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(castOp, llvmDstTy,
1549 llvmSrcVal);
1550 else
1551 rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy,
1552 llvmSrcVal);
1553 return mlir::success();
1554 }
1555 case cir::CastKind::float_to_int: {
1556 mlir::Type dstTy = castOp.getType();
1557 mlir::Value llvmSrcVal = adaptor.getSrc();
1558 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1559 if (mlir::cast<cir::IntType>(elementTypeIfVector(castOp.getType()))
1560 .isSigned())
1561 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(castOp, llvmDstTy,
1562 llvmSrcVal);
1563 else
1564 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(castOp, llvmDstTy,
1565 llvmSrcVal);
1566 return mlir::success();
1567 }
1568 case cir::CastKind::bitcast: {
1569 mlir::Type dstTy = castOp.getType();
1570 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1571
1572 assert(!MissingFeatures::cxxABI());
1574
1575 mlir::Value llvmSrcVal = adaptor.getSrc();
1576 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(castOp, llvmDstTy,
1577 llvmSrcVal);
1578 return mlir::success();
1579 }
1580 case cir::CastKind::ptr_to_bool: {
1581 mlir::Value llvmSrcVal = adaptor.getSrc();
1582 mlir::Value zeroPtr = mlir::LLVM::ZeroOp::create(rewriter, castOp.getLoc(),
1583 llvmSrcVal.getType());
1584 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1585 castOp, mlir::LLVM::ICmpPredicate::ne, llvmSrcVal, zeroPtr);
1586 break;
1587 }
1588 case cir::CastKind::address_space: {
1589 mlir::Type dstTy = castOp.getType();
1590 mlir::Value llvmSrcVal = adaptor.getSrc();
1591 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1592 rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>(castOp, llvmDstTy,
1593 llvmSrcVal);
1594 break;
1595 }
1596 case cir::CastKind::member_ptr_to_bool:
1597 assert(!MissingFeatures::cxxABI());
1598 assert(!MissingFeatures::methodType());
1599 break;
1600 default: {
1601 return castOp.emitError("Unhandled cast kind: ")
1602 << castOp.getKindAttrName();
1603 }
1604 }
1605
1606 return mlir::success();
1607}
1608
1609mlir::LogicalResult CIRToLLVMBuiltinIntCastOpLowering::matchAndRewrite(
1610 cir::BuiltinIntCastOp op, OpAdaptor adaptor,
1611 mlir::ConversionPatternRewriter &rewriter) const {
1612 // Both the CIR integer and the builtin integer/index lower to LLVM integer
1613 // types, so this cast becomes an integer resize. Signedness is taken from
1614 // the CIR integer side (the builtin/index side is treated as signless).
1615 bool isUnsigned = true;
1616 if (auto cirSrc = mlir::dyn_cast<cir::IntType>(op.getSrc().getType()))
1617 isUnsigned = cirSrc.isUnsigned();
1618 else if (auto cirDst = mlir::dyn_cast<cir::IntType>(op.getType()))
1619 isUnsigned = cirDst.isUnsigned();
1620
1621 mlir::Value llvmSrc = adaptor.getSrc();
1622 mlir::Type llvmDstTy = getTypeConverter()->convertType(op.getType());
1623 auto srcIntTy = mlir::cast<mlir::IntegerType>(llvmSrc.getType());
1624 auto dstIntTy = mlir::cast<mlir::IntegerType>(llvmDstTy);
1625 unsigned srcWidth = srcIntTy.getWidth();
1626 unsigned dstWidth = dstIntTy.getWidth();
1627
1628 // Fixed-width builtin integers must match the CIR integer width.
1629 // If the converted LLVM widths differ, the non-CIR side must have been
1630 // 'index' type (target dependent width).
1631 assert((srcWidth == dstWidth ||
1632 mlir::isa<mlir::IndexType>(op.getSrc().getType()) ||
1633 mlir::isa<mlir::IndexType>(op.getType())) &&
1634 "only index casts may change width during lowering");
1635
1636 // For equal widths getLLVMIntCast returns the source unchanged, so casts
1637 // between CIR integers and fixed-width builtin integers lower to a no-op.
1638 rewriter.replaceOp(op, getLLVMIntCast(rewriter, llvmSrc, dstIntTy, isUnsigned,
1639 srcWidth, dstWidth));
1640 return mlir::success();
1641}
1642
1643static mlir::Value convertToIndexTy(mlir::ConversionPatternRewriter &rewriter,
1644 mlir::ModuleOp mod, mlir::Value index,
1645 mlir::Type baseTy, cir::IntType strideTy) {
1646 mlir::Operation *indexOp = index.getDefiningOp();
1647 if (!indexOp)
1648 return index;
1649
1650 auto indexType = mlir::cast<mlir::IntegerType>(index.getType());
1651 mlir::DataLayout llvmLayout(mod);
1652 std::optional<uint64_t> layoutWidth = llvmLayout.getTypeIndexBitwidth(baseTy);
1653
1654 // If there is no change in width, don't do anything.
1655 if (!layoutWidth || *layoutWidth == indexType.getWidth())
1656 return index;
1657
1658 // If the index comes from a subtraction, make sure the extension happens
1659 // before it. To achieve that, look at unary minus, which already got
1660 // lowered to "sub 0, x".
1661 auto sub = dyn_cast<mlir::LLVM::SubOp>(indexOp);
1662 bool rewriteSub = false;
1663 if (sub) {
1664 if (auto lhsConst =
1665 dyn_cast<mlir::LLVM::ConstantOp>(sub.getLhs().getDefiningOp())) {
1666 auto lhsConstInt = mlir::dyn_cast<mlir::IntegerAttr>(lhsConst.getValue());
1667 if (lhsConstInt && lhsConstInt.getValue() == 0) {
1668 index = sub.getRhs();
1669 rewriteSub = true;
1670 }
1671 }
1672 }
1673
1674 auto llvmDstType = rewriter.getIntegerType(*layoutWidth);
1675 bool isUnsigned = strideTy && strideTy.isUnsigned();
1676 index = getLLVMIntCast(rewriter, index, llvmDstType, isUnsigned,
1677 indexType.getWidth(), *layoutWidth);
1678
1679 if (rewriteSub) {
1680 index = mlir::LLVM::SubOp::create(
1681 rewriter, index.getLoc(),
1682 mlir::LLVM::ConstantOp::create(rewriter, index.getLoc(),
1683 index.getType(), 0),
1684 index);
1685 // TODO: ensure sub is trivially dead now.
1686 rewriter.eraseOp(sub);
1687 }
1688
1689 return index;
1690}
1691
1692mlir::LogicalResult CIRToLLVMPtrStrideOpLowering::matchAndRewrite(
1693 cir::PtrStrideOp ptrStrideOp, OpAdaptor adaptor,
1694 mlir::ConversionPatternRewriter &rewriter) const {
1695
1696 const mlir::TypeConverter *tc = getTypeConverter();
1697 const mlir::Type resultTy = tc->convertType(ptrStrideOp.getType());
1698
1699 mlir::Type elementTy =
1700 convertTypeForMemory(*tc, dataLayout, ptrStrideOp.getElementType());
1701
1702 // void and function types doesn't really have a layout to use in GEPs,
1703 // make it i8 instead.
1704 if (mlir::isa<mlir::LLVM::LLVMVoidType>(elementTy) ||
1705 mlir::isa<mlir::LLVM::LLVMFunctionType>(elementTy))
1706 elementTy = mlir::IntegerType::get(elementTy.getContext(), 8,
1707 mlir::IntegerType::Signless);
1708 // Zero-extend, sign-extend or trunc the pointer value.
1709 mlir::Value index = adaptor.getStride();
1710 index = convertToIndexTy(
1711 rewriter, ptrStrideOp->getParentOfType<mlir::ModuleOp>(), index,
1712 adaptor.getBase().getType(),
1713 dyn_cast<cir::IntType>(ptrStrideOp.getOperand(1).getType()));
1714
1715 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1716 ptrStrideOp, resultTy, elementTy, adaptor.getBase(), index);
1717 return mlir::success();
1718}
1719
1720mlir::LogicalResult CIRToLLVMGetElementOpLowering::matchAndRewrite(
1721 cir::GetElementOp op, OpAdaptor adaptor,
1722 mlir::ConversionPatternRewriter &rewriter) const {
1723 if (auto arrayTy =
1724 mlir::dyn_cast<cir::ArrayType>(op.getBaseType().getPointee())) {
1725 const mlir::TypeConverter *converter = getTypeConverter();
1726 const mlir::Type llArrayTy = converter->convertType(arrayTy);
1727 const mlir::Type llResultTy = converter->convertType(op.getType());
1728 mlir::Type elementTy =
1729 convertTypeForMemory(*converter, dataLayout, op.getElementType());
1730
1731 // void and function types don't really have a layout to use in GEPs,
1732 // make it i8 instead.
1733 if (mlir::isa<mlir::LLVM::LLVMVoidType>(elementTy) ||
1734 mlir::isa<mlir::LLVM::LLVMFunctionType>(elementTy))
1735 elementTy = rewriter.getIntegerType(8);
1736
1737 mlir::Value index = adaptor.getIndex();
1738 index =
1739 convertToIndexTy(rewriter, op->getParentOfType<mlir::ModuleOp>(), index,
1740 adaptor.getBase().getType(),
1741 dyn_cast<cir::IntType>(op.getOperand(1).getType()));
1742
1743 // Since the base address is a pointer to an aggregate, the first
1744 // offset is always zero. The second offset tell us which member it
1745 // will access.
1746 std::array<mlir::LLVM::GEPArg, 2> offset{0, index};
1747 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(op, llResultTy, llArrayTy,
1748 adaptor.getBase(), offset);
1749 return mlir::success();
1750 }
1751
1752 op.emitError() << "NYI: GetElementOp lowering to LLVM for non-array";
1753 return mlir::failure();
1754}
1755
1756mlir::LogicalResult CIRToLLVMBaseClassAddrOpLowering::matchAndRewrite(
1757 cir::BaseClassAddrOp baseClassOp, OpAdaptor adaptor,
1758 mlir::ConversionPatternRewriter &rewriter) const {
1759 const mlir::Type resultType =
1760 getTypeConverter()->convertType(baseClassOp.getType());
1761 mlir::Value derivedAddr = adaptor.getDerivedAddr();
1762 llvm::SmallVector<mlir::LLVM::GEPArg, 1> offset = {
1763 adaptor.getOffset().getZExtValue()};
1764 mlir::Type byteType = mlir::IntegerType::get(resultType.getContext(), 8,
1765 mlir::IntegerType::Signless);
1766 if (adaptor.getOffset().getZExtValue() == 0) {
1767 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(
1768 baseClassOp, resultType, adaptor.getDerivedAddr());
1769 return mlir::success();
1770 }
1771
1772 if (baseClassOp.getAssumeNotNull()) {
1773 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1774 baseClassOp, resultType, byteType, derivedAddr, offset);
1775 } else {
1776 auto loc = baseClassOp.getLoc();
1777 mlir::Value isNull = mlir::LLVM::ICmpOp::create(
1778 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, derivedAddr,
1779 mlir::LLVM::ZeroOp::create(rewriter, loc, derivedAddr.getType()));
1780 mlir::Value adjusted = mlir::LLVM::GEPOp::create(
1781 rewriter, loc, resultType, byteType, derivedAddr, offset);
1782 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(baseClassOp, isNull,
1783 derivedAddr, adjusted);
1784 }
1785 return mlir::success();
1786}
1787
1788mlir::LogicalResult CIRToLLVMDerivedClassAddrOpLowering::matchAndRewrite(
1789 cir::DerivedClassAddrOp derivedClassOp, OpAdaptor adaptor,
1790 mlir::ConversionPatternRewriter &rewriter) const {
1791 const mlir::Type resultType =
1792 getTypeConverter()->convertType(derivedClassOp.getType());
1793 mlir::Value baseAddr = adaptor.getBaseAddr();
1794 // The offset is set in the operation as an unsigned value, but it must be
1795 // applied as a negative offset.
1796 int64_t offsetVal = -(adaptor.getOffset().getZExtValue());
1797 if (offsetVal == 0) {
1798 // If the offset is zero, we can just return the base address,
1799 rewriter.replaceOp(derivedClassOp, baseAddr);
1800 return mlir::success();
1801 }
1802 llvm::SmallVector<mlir::LLVM::GEPArg, 1> offset = {offsetVal};
1803 mlir::Type byteType = mlir::IntegerType::get(resultType.getContext(), 8,
1804 mlir::IntegerType::Signless);
1805 if (derivedClassOp.getAssumeNotNull()) {
1806 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1807 derivedClassOp, resultType, byteType, baseAddr, offset,
1808 mlir::LLVM::GEPNoWrapFlags::inbounds);
1809 } else {
1810 mlir::Location loc = derivedClassOp.getLoc();
1811 mlir::Value isNull = mlir::LLVM::ICmpOp::create(
1812 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, baseAddr,
1813 mlir::LLVM::ZeroOp::create(rewriter, loc, baseAddr.getType()));
1814 mlir::Value adjusted =
1815 mlir::LLVM::GEPOp::create(rewriter, loc, resultType, byteType, baseAddr,
1816 offset, mlir::LLVM::GEPNoWrapFlags::inbounds);
1817 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(derivedClassOp, isNull,
1818 baseAddr, adjusted);
1819 }
1820 return mlir::success();
1821}
1822
1823mlir::LogicalResult CIRToLLVMFMaxNumOpLowering::matchAndRewrite(
1824 cir::FMaxNumOp op, OpAdaptor adaptor,
1825 mlir::ConversionPatternRewriter &rewriter) const {
1826 mlir::Type resTy = typeConverter->convertType(op.getType());
1827 rewriter.replaceOpWithNewOp<mlir::LLVM::MaxNumOp>(
1828 op, resTy, adaptor.getLhs(), adaptor.getRhs(),
1829 mlir::LLVM::FastmathFlags::nsz);
1830 return mlir::success();
1831}
1832
1833mlir::LogicalResult CIRToLLVMFMinNumOpLowering::matchAndRewrite(
1834 cir::FMinNumOp op, OpAdaptor adaptor,
1835 mlir::ConversionPatternRewriter &rewriter) const {
1836 mlir::Type resTy = typeConverter->convertType(op.getType());
1837 rewriter.replaceOpWithNewOp<mlir::LLVM::MinNumOp>(
1838 op, resTy, adaptor.getLhs(), adaptor.getRhs(),
1839 mlir::LLVM::FastmathFlags::nsz);
1840 return mlir::success();
1841}
1842
1843mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite(
1844 cir::AllocaOp op, OpAdaptor adaptor,
1845 mlir::ConversionPatternRewriter &rewriter) const {
1846 mlir::Value size =
1847 op.isDynamic()
1848 ? adaptor.getDynAllocSize()
1849 : mlir::LLVM::ConstantOp::create(
1850 rewriter, op.getLoc(),
1851 typeConverter->convertType(rewriter.getIndexType()), 1);
1852 mlir::Type elementTy =
1853 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getAllocaType());
1854 if (!elementTy)
1855 return op.emitError()
1856 << "NYI: lowering alloca of a type with no memory representation";
1857 mlir::Type resultTy =
1858 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getType());
1859
1862
1863 rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(op, resultTy, elementTy,
1864 size, op.getAlignment());
1865
1866 return mlir::success();
1867}
1868
1869mlir::LogicalResult CIRToLLVMRotateOpLowering::matchAndRewrite(
1870 cir::RotateOp op, OpAdaptor adaptor,
1871 mlir::ConversionPatternRewriter &rewriter) const {
1872 // Note that LLVM intrinsic calls to @llvm.fsh{r,l}.i* have the same type as
1873 // the operand.
1874 mlir::Value input = adaptor.getInput();
1875 if (op.isRotateLeft())
1876 rewriter.replaceOpWithNewOp<mlir::LLVM::FshlOp>(op, input, input,
1877 adaptor.getAmount());
1878 else
1879 rewriter.replaceOpWithNewOp<mlir::LLVM::FshrOp>(op, input, input,
1880 adaptor.getAmount());
1881 return mlir::LogicalResult::success();
1882}
1883
1884/// The `llvm.byval`, `llvm.sret`, and `llvm.byref` argument attributes carry
1885/// the pointee type as a TypeAttr. After the CallConvLowering pass that type
1886/// is still a CIR record; remap it to the lowered LLVM type so translation to
1887/// LLVM IR does not encounter a CIR type in an attribute. Returns the input
1888/// unchanged when there is nothing to convert.
1889static mlir::ArrayAttr
1890convertTypedArgAttrs(mlir::ArrayAttr argAttrs,
1891 const mlir::TypeConverter &converter,
1892 mlir::MLIRContext *ctx) {
1893 if (!argAttrs)
1894 return argAttrs;
1895 bool changed = false;
1896 SmallVector<mlir::Attribute> loweredArgAttrs;
1897 loweredArgAttrs.reserve(argAttrs.size());
1898 for (mlir::Attribute a : argAttrs) {
1899 auto dict = cast<mlir::DictionaryAttr>(a);
1900 SmallVector<mlir::NamedAttribute> entries(dict.begin(), dict.end());
1901 for (mlir::NamedAttribute &entry : entries) {
1902 StringRef name = entry.getName().strref();
1903 if (name != mlir::LLVM::LLVMDialect::getByValAttrName() &&
1904 name != mlir::LLVM::LLVMDialect::getStructRetAttrName() &&
1905 name != mlir::LLVM::LLVMDialect::getByRefAttrName())
1906 continue;
1907 auto typeAttr = dyn_cast<mlir::TypeAttr>(entry.getValue());
1908 if (!typeAttr)
1909 continue;
1910 mlir::Type lowered = converter.convertType(typeAttr.getValue());
1911 if (lowered && lowered != typeAttr.getValue()) {
1912 entry.setValue(mlir::TypeAttr::get(lowered));
1913 changed = true;
1914 }
1915 }
1916 loweredArgAttrs.push_back(mlir::DictionaryAttr::get(ctx, entries));
1917 }
1918 return changed ? mlir::ArrayAttr::get(ctx, loweredArgAttrs) : argAttrs;
1919}
1920
1921static void lowerCallAttributes(cir::CIRCallOpInterface op,
1922 const mlir::TypeConverter &converter,
1924 for (mlir::NamedAttribute attr : op->getAttrs()) {
1925 if (attr.getName() == CIRDialect::getCalleeAttrName() ||
1926 attr.getName() == CIRDialect::getSideEffectAttrName() ||
1927 attr.getName() == CIRDialect::getNoThrowAttrName() ||
1928 attr.getName() == CIRDialect::getNoUnwindAttrName() ||
1929 attr.getName() == CIRDialect::getNoReturnAttrName() ||
1930 attr.getName() == CIRDialect::getMustTailAttrName())
1931 continue;
1932
1934 if (attr.getName() == CIRDialect::getArgAttrsAttrName()) {
1935 auto argAttrs = cast<mlir::ArrayAttr>(attr.getValue());
1936 result.emplace_back(
1937 attr.getName(),
1938 convertTypedArgAttrs(argAttrs, converter, op->getContext()));
1939 continue;
1940 }
1941 result.push_back(attr);
1942 }
1943}
1944
1945static mlir::LogicalResult
1946rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
1947 mlir::ConversionPatternRewriter &rewriter,
1948 const mlir::TypeConverter *converter,
1949 mlir::SymbolTableCollection &symbolTables,
1950 mlir::FlatSymbolRefAttr calleeAttr,
1951 mlir::Block *continueBlock = nullptr,
1952 mlir::Block *landingPadBlock = nullptr) {
1954 mlir::ValueTypeRange<mlir::ResultRange> cirResults = op->getResultTypes();
1955 auto call = cast<cir::CIRCallOpInterface>(op);
1956
1957 if (converter->convertTypes(cirResults, llvmResults).failed())
1958 return mlir::failure();
1959
1961
1962 mlir::LLVM::MemoryEffectsAttr memoryEffects;
1963 bool noUnwind = false;
1964 bool willReturn = false;
1965 bool noReturn = false;
1966 convertSideEffectForCall(op, call.getNothrow(), call.getSideEffect(),
1967 memoryEffects, noUnwind, willReturn, noReturn);
1968
1970 lowerCallAttributes(call, *converter, attributes);
1971
1972 mlir::LLVM::LLVMFunctionType llvmFnTy;
1973
1974 // Temporary to handle the case where we need to prepend an operand if the
1975 // callee is an alias.
1976 SmallVector<mlir::Value> adjustedCallOperands;
1977
1978 if (calleeAttr) { // direct call
1979 mlir::Operation *callee =
1980 symbolTables.lookupNearestSymbolFrom(op, calleeAttr);
1981 if (auto fn = mlir::dyn_cast<mlir::FunctionOpInterface>(callee)) {
1982 llvmFnTy = converter->convertType<mlir::LLVM::LLVMFunctionType>(
1983 fn.getFunctionType());
1984 assert(llvmFnTy && "Failed to convert function type");
1985 } else if (auto alias = mlir::cast<mlir::LLVM::AliasOp>(callee)) {
1986 // If the callee was an alias. In that case,
1987 // we need to prepend the address of the alias to the operands. The
1988 // way aliases work in the LLVM dialect is a little counter-intuitive.
1989 // The AliasOp itself is a pseudo-function that returns the address of
1990 // the global value being aliased, but when we generate the call we
1991 // need to insert an operation that gets the address of the AliasOp.
1992 // This all gets sorted out when the LLVM dialect is lowered to LLVM IR.
1993 auto symAttr = mlir::cast<mlir::FlatSymbolRefAttr>(calleeAttr);
1994 auto addrOfAlias =
1995 mlir::LLVM::AddressOfOp::create(
1996 rewriter, op->getLoc(),
1997 mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symAttr)
1998 .getResult();
1999 adjustedCallOperands.push_back(addrOfAlias);
2000
2001 // Now add the regular operands and assign this to the range value.
2002 llvm::append_range(adjustedCallOperands, callOperands);
2003 callOperands = adjustedCallOperands;
2004
2005 // Clear the callee attribute because we're calling an alias.
2006 calleeAttr = {};
2007 llvmFnTy = mlir::cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
2008 } else {
2009 // Was this an ifunc?
2010 return op->emitError("Unexpected callee type!");
2011 }
2012 } else { // indirect call
2013 assert(!op->getOperands().empty() &&
2014 "operands list must no be empty for the indirect call");
2015 auto calleeTy = op->getOperands().front().getType();
2016 auto calleePtrTy = cast<cir::PointerType>(calleeTy);
2017 auto calleeFuncTy = cast<cir::FuncType>(calleePtrTy.getPointee());
2018 llvm::append_range(adjustedCallOperands, callOperands);
2019 llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
2020 converter->convertType(calleeFuncTy));
2021 }
2022
2024
2025 if (landingPadBlock) {
2026 auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::InvokeOp>(
2027 op, llvmFnTy, calleeAttr, callOperands, continueBlock,
2028 mlir::ValueRange{}, landingPadBlock, mlir::ValueRange{});
2029 newOp->setAttrs(attributes);
2030 } else {
2031 auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
2032 op, llvmFnTy, calleeAttr, callOperands);
2033 newOp->setAttrs(attributes);
2034 if (memoryEffects)
2035 newOp.setMemoryEffectsAttr(memoryEffects);
2036 newOp.setNoUnwind(noUnwind);
2037 newOp.setWillReturn(willReturn);
2038 newOp.setNoreturn(noReturn);
2039 if (op->hasAttr(CIRDialect::getMustTailAttrName()))
2040 newOp.setTailCallKind(mlir::LLVM::TailCallKind::MustTail);
2041 }
2042
2043 return mlir::success();
2044}
2045
2046mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite(
2047 cir::CallOp op, OpAdaptor adaptor,
2048 mlir::ConversionPatternRewriter &rewriter) const {
2049 return rewriteCallOrInvoke(op.getOperation(), adaptor.getOperands(), rewriter,
2050 getTypeConverter(), symbolTables,
2051 op.getCalleeAttr());
2052}
2053
2054mlir::LogicalResult CIRToLLVMTryCallOpLowering::matchAndRewrite(
2055 cir::TryCallOp op, OpAdaptor adaptor,
2056 mlir::ConversionPatternRewriter &rewriter) const {
2058 return rewriteCallOrInvoke(
2059 op.getOperation(), adaptor.getOperands(), rewriter, getTypeConverter(),
2060 symbolTables, op.getCalleeAttr(), op.getNormalDest(), op.getUnwindDest());
2061}
2062
2063mlir::LogicalResult CIRToLLVMReturnAddrOpLowering::matchAndRewrite(
2064 cir::ReturnAddrOp op, OpAdaptor adaptor,
2065 mlir::ConversionPatternRewriter &rewriter) const {
2066 const mlir::Type llvmPtrTy = getTypeConverter()->convertType(op.getType());
2067 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.returnaddress",
2068 llvmPtrTy, adaptor.getOperands());
2069 return mlir::success();
2070}
2071
2072mlir::LogicalResult CIRToLLVMFrameAddrOpLowering::matchAndRewrite(
2073 cir::FrameAddrOp op, OpAdaptor adaptor,
2074 mlir::ConversionPatternRewriter &rewriter) const {
2075 const mlir::Type llvmPtrTy = getTypeConverter()->convertType(op.getType());
2076 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.frameaddress", llvmPtrTy,
2077 adaptor.getOperands());
2078 return mlir::success();
2079}
2080
2081mlir::LogicalResult CIRToLLVMClearCacheOpLowering::matchAndRewrite(
2082 cir::ClearCacheOp op, OpAdaptor adaptor,
2083 mlir::ConversionPatternRewriter &rewriter) const {
2084 mlir::Value begin = adaptor.getBegin();
2085 mlir::Value end = adaptor.getEnd();
2086 auto intrinNameAttr =
2087 mlir::StringAttr::get(op.getContext(), "llvm.clear_cache");
2088 rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
2089 op, mlir::Type{}, intrinNameAttr, mlir::ValueRange{begin, end});
2090
2091 return mlir::success();
2092}
2093
2094mlir::LogicalResult CIRToLLVMAddrOfReturnAddrOpLowering::matchAndRewrite(
2095 cir::AddrOfReturnAddrOp op, OpAdaptor adaptor,
2096 mlir::ConversionPatternRewriter &rewriter) const {
2097 const mlir::Type llvmPtrTy = getTypeConverter()->convertType(op.getType());
2098 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.addressofreturnaddress",
2099 llvmPtrTy, adaptor.getOperands());
2100 return mlir::success();
2101}
2102
2103mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
2104 cir::LoadOp op, OpAdaptor adaptor,
2105 mlir::ConversionPatternRewriter &rewriter) const {
2106 const mlir::Type llvmTy =
2107 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getType());
2108 if (!llvmTy)
2109 return op.emitError()
2110 << "NYI: lowering load of a type with no memory representation";
2111 mlir::LLVM::AtomicOrdering ordering = getLLVMMemOrder(op.getMemOrder());
2112 std::optional<size_t> opAlign = op.getAlignment();
2113 unsigned alignment = (unsigned)opAlign.value_or(
2114 getMemoryFallbackAlignment(op.getType(), llvmTy, dataLayout));
2115
2117
2118 std::optional<llvm::StringRef> llvmSyncScope =
2119 getLLVMSyncScope(op.getSyncScope());
2120
2121 mlir::LLVM::LoadOp newLoad = mlir::LLVM::LoadOp::create(
2122 rewriter, op->getLoc(), llvmTy, adaptor.getAddr(), alignment,
2123 op.getIsVolatile(), /*isNonTemporal=*/op.getIsNontemporal(),
2124 /*isInvariant=*/op.getInvariant(), /*isInvariantGroup=*/false, ordering,
2125 llvmSyncScope.value_or(std::string()));
2126 if (mlir::Attribute domain = op->getAttr("cir.riscv_nontemporal_domain"))
2127 newLoad->setAttr("cir.riscv_nontemporal_domain", domain);
2128
2129 // Convert adapted result to its original type if needed.
2130 mlir::Value result =
2131 emitFromMemory(rewriter, dataLayout, op, newLoad.getResult());
2132 rewriter.replaceOp(op, result);
2134 return mlir::LogicalResult::success();
2135}
2136
2137mlir::LogicalResult
2138cir::direct::CIRToLLVMVecMaskedLoadOpLowering::matchAndRewrite(
2139 cir::VecMaskedLoadOp op, OpAdaptor adaptor,
2140 mlir::ConversionPatternRewriter &rewriter) const {
2141 const mlir::Type llvmResTy =
2142 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getType());
2143 if (!llvmResTy)
2144 return op.emitError()
2145 << "NYI: lowering masked load of a type with no memory "
2146 "representation";
2147
2148 std::optional<size_t> opAlign = op.getAlignment();
2149 unsigned alignment =
2150 (unsigned)opAlign.value_or(dataLayout.getTypeABIAlignment(llvmResTy));
2151
2152 mlir::IntegerAttr alignAttr = rewriter.getI32IntegerAttr(alignment);
2153
2154 auto newLoad = mlir::LLVM::MaskedLoadOp::create(
2155 rewriter, op.getLoc(), llvmResTy, adaptor.getAddr(), adaptor.getMask(),
2156 adaptor.getPassThru(), alignAttr);
2157
2158 rewriter.replaceOp(op, newLoad.getResult());
2159 return mlir::success();
2160}
2161
2162mlir::LogicalResult CIRToLLVMStoreOpLowering::matchAndRewrite(
2163 cir::StoreOp op, OpAdaptor adaptor,
2164 mlir::ConversionPatternRewriter &rewriter) const {
2165 mlir::LLVM::AtomicOrdering memorder = getLLVMMemOrder(op.getMemOrder());
2166 mlir::Type valueType = op.getValue().getType();
2167 const mlir::Type llvmTy =
2168 convertTypeForMemory(*getTypeConverter(), dataLayout, valueType);
2169 if (!llvmTy)
2170 return op.emitError()
2171 << "NYI: lowering store of a type with no memory representation";
2172 std::optional<size_t> opAlign = op.getAlignment();
2173 unsigned alignment = (unsigned)opAlign.value_or(
2174 getMemoryFallbackAlignment(valueType, llvmTy, dataLayout));
2175
2177
2178 // Convert adapted value to its memory type if needed.
2179 mlir::Value value = emitToMemory(rewriter, dataLayout,
2180 op.getValue().getType(), adaptor.getValue());
2182
2183 std::optional<llvm::StringRef> llvmSyncScope =
2184 getLLVMSyncScope(op.getSyncScope());
2185
2186 mlir::LLVM::StoreOp storeOp = mlir::LLVM::StoreOp::create(
2187 rewriter, op->getLoc(), value, adaptor.getAddr(), alignment,
2188 op.getIsVolatile(),
2189 /*isNonTemporal=*/op.getIsNontemporal(), /*isInvariantGroup=*/false,
2190 memorder, llvmSyncScope.value_or(std::string()));
2191 if (mlir::Attribute domain = op->getAttr("cir.riscv_nontemporal_domain"))
2192 storeOp->setAttr("cir.riscv_nontemporal_domain", domain);
2193 rewriter.replaceOp(op, storeOp);
2195 return mlir::LogicalResult::success();
2196}
2197
2198static mlir::Type getConstArrayBaseElementType(mlir::Type ty) {
2199 while (auto arrTy = mlir::dyn_cast<cir::ArrayType>(ty))
2200 ty = arrTy.getElementType();
2201 return ty;
2202}
2203
2204static bool isBulkLowerableConstArrayBaseElement(mlir::Type baseElemTy) {
2205 return mlir::isa<cir::PointerType, cir::IntType, cir::BoolType,
2206 cir::FPTypeInterface>(baseElemTy);
2207}
2208
2209mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
2210 cir::ConstantOp op, OpAdaptor adaptor,
2211 mlir::ConversionPatternRewriter &rewriter) const {
2212 mlir::Attribute attr = op.getValue();
2213
2214 if (mlir::isa<cir::PoisonAttr>(attr)) {
2215 rewriter.replaceOpWithNewOp<mlir::LLVM::PoisonOp>(
2216 op, getTypeConverter()->convertType(op.getType()));
2217 return mlir::success();
2218 }
2219
2220 if (mlir::isa<cir::UndefAttr>(attr)) {
2221 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
2222 op, getTypeConverter()->convertType(op.getType()));
2223 return mlir::success();
2224 }
2225
2226 if (mlir::isa<mlir::IntegerType>(op.getType())) {
2227 // Verified cir.const operations cannot actually be of these types, but the
2228 // lowering pass may generate temporary cir.const operations with these
2229 // types. This is OK since MLIR allows unverified operations to be alive
2230 // during a pass as long as they don't live past the end of the pass.
2231 attr = op.getValue();
2232 } else if (mlir::isa<cir::BoolType>(op.getType())) {
2233 int value = mlir::cast<cir::BoolAttr>(op.getValue()).getValue();
2234 attr = rewriter.getIntegerAttr(typeConverter->convertType(op.getType()),
2235 value);
2236 } else if (mlir::isa<cir::IntType>(op.getType())) {
2237 // Lower GlobalViewAttr to llvm.mlir.addressof + llvm.mlir.ptrtoint
2238 if (auto ga = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) {
2239 // We can have a global view with an integer type in the case of method
2240 // pointers, but the lowering of those doesn't go through this path.
2241 // They are handled in the visitCirAttr. This is left as an error until
2242 // we have a test case that reaches it.
2244 op.emitError() << "global view with integer type";
2245 return mlir::failure();
2246 }
2247
2248 attr = rewriter.getIntegerAttr(
2249 typeConverter->convertType(op.getType()),
2250 mlir::cast<cir::IntAttr>(op.getValue()).getValue());
2251 } else if (mlir::isa<cir::FPTypeInterface>(op.getType())) {
2252 attr = rewriter.getFloatAttr(
2253 typeConverter->convertType(op.getType()),
2254 mlir::cast<cir::FPAttr>(op.getValue()).getValue());
2255 } else if (mlir::isa<cir::PointerType>(op.getType())) {
2256 // Optimize with dedicated LLVM op for null pointers.
2257 if (mlir::isa<cir::ConstPtrAttr>(op.getValue())) {
2258 if (mlir::cast<cir::ConstPtrAttr>(op.getValue()).isNullValue()) {
2259 rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(
2260 op, typeConverter->convertType(op.getType()));
2261 return mlir::success();
2262 }
2263 }
2264 // Lower GlobalViewAttr to llvm.mlir.addressof
2265 if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) {
2266 auto newOp = lowerCirAttrAsValue(op, gv, rewriter, getTypeConverter());
2267 rewriter.replaceOp(op, newOp);
2268 return mlir::success();
2269 }
2270 attr = op.getValue();
2271 } else if (const auto arrTy = mlir::dyn_cast<cir::ArrayType>(op.getType())) {
2272 const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(op.getValue());
2273 if (!constArr && !isa<cir::ZeroAttr, cir::UndefAttr>(op.getValue()))
2274 return op.emitError() << "array does not have a constant initializer";
2275
2276 std::optional<mlir::Attribute> denseAttr;
2277 if (constArr &&
2278 (denseAttr = lowerConstArrayAttr(constArr, typeConverter))) {
2279 attr = denseAttr.value();
2280 } else {
2281 const mlir::Value initVal =
2282 lowerCirAttrAsValue(op, op.getValue(), rewriter, typeConverter);
2283 rewriter.replaceOp(op, initVal);
2284 return mlir::success();
2285 }
2286 } else if (const auto recordAttr =
2287 mlir::dyn_cast<cir::ConstRecordAttr>(op.getValue())) {
2288 auto initVal = lowerCirAttrAsValue(op, recordAttr, rewriter, typeConverter);
2289 rewriter.replaceOp(op, initVal);
2290 return mlir::success();
2291 } else if (const auto vecTy = mlir::dyn_cast<cir::VectorType>(op.getType())) {
2292 rewriter.replaceOp(op, lowerCirAttrAsValue(op, op.getValue(), rewriter,
2293 getTypeConverter()));
2294 return mlir::success();
2295 } else if (mlir::isa<cir::RecordType>(op.getType())) {
2296 if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(attr)) {
2297 mlir::Value initVal =
2298 lowerCirAttrAsValue(op, attr, rewriter, typeConverter);
2299 rewriter.replaceOp(op, initVal);
2300 return mlir::success();
2301 }
2302 return op.emitError() << "unsupported lowering for record constant type "
2303 << op.getType();
2304 } else if (auto complexTy = mlir::dyn_cast<cir::ComplexType>(op.getType())) {
2305 mlir::Type complexElemTy = complexTy.getElementType();
2306 mlir::Type complexElemLLVMTy = typeConverter->convertType(complexElemTy);
2307
2308 if (auto zeroInitAttr = mlir::dyn_cast<cir::ZeroAttr>(op.getValue())) {
2309 mlir::TypedAttr zeroAttr = rewriter.getZeroAttr(complexElemLLVMTy);
2310 mlir::ArrayAttr array = rewriter.getArrayAttr({zeroAttr, zeroAttr});
2311 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
2312 op, getTypeConverter()->convertType(op.getType()), array);
2313 return mlir::success();
2314 }
2315
2316 if (mlir::isa<cir::UndefAttr>(op.getValue())) {
2317 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
2318 op, getTypeConverter()->convertType(op.getType()));
2319 return mlir::success();
2320 }
2321
2322 auto complexAttr = mlir::cast<cir::ConstComplexAttr>(op.getValue());
2323
2324 mlir::Attribute components[2];
2325 if (mlir::isa<cir::IntType>(complexElemTy)) {
2326 components[0] = rewriter.getIntegerAttr(
2327 complexElemLLVMTy,
2328 mlir::cast<cir::IntAttr>(complexAttr.getReal()).getValue());
2329 components[1] = rewriter.getIntegerAttr(
2330 complexElemLLVMTy,
2331 mlir::cast<cir::IntAttr>(complexAttr.getImag()).getValue());
2332 } else {
2333 components[0] = rewriter.getFloatAttr(
2334 complexElemLLVMTy,
2335 mlir::cast<cir::FPAttr>(complexAttr.getReal()).getValue());
2336 components[1] = rewriter.getFloatAttr(
2337 complexElemLLVMTy,
2338 mlir::cast<cir::FPAttr>(complexAttr.getImag()).getValue());
2339 }
2340
2341 attr = rewriter.getArrayAttr(components);
2342 } else {
2343 return op.emitError() << "unsupported constant type " << op.getType();
2344 }
2345
2346 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
2347 op, getTypeConverter()->convertType(op.getType()), attr);
2348
2349 return mlir::success();
2350}
2351
2352static uint64_t getTypeSize(mlir::Type type, mlir::Operation &op) {
2353 mlir::DataLayout layout(op.getParentOfType<mlir::ModuleOp>());
2354 // For LLVM purposes we treat void as u8.
2355 if (isa<cir::VoidType>(type))
2356 type = cir::IntType::get(type.getContext(), 8, /*isSigned=*/false);
2357 return llvm::divideCeil(layout.getTypeSizeInBits(type), 8);
2358}
2359
2360mlir::LogicalResult CIRToLLVMPrefetchOpLowering::matchAndRewrite(
2361 cir::PrefetchOp op, OpAdaptor adaptor,
2362 mlir::ConversionPatternRewriter &rewriter) const {
2363 rewriter.replaceOpWithNewOp<mlir::LLVM::Prefetch>(
2364 op, adaptor.getAddr(), adaptor.getIsWrite(), adaptor.getLocality(),
2365 /*DataCache=*/1);
2366 return mlir::success();
2367}
2368
2369mlir::LogicalResult CIRToLLVMPtrDiffOpLowering::matchAndRewrite(
2370 cir::PtrDiffOp op, OpAdaptor adaptor,
2371 mlir::ConversionPatternRewriter &rewriter) const {
2372 auto dstTy = mlir::cast<cir::IntType>(op.getType());
2373 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
2374
2375 auto lhs = mlir::LLVM::PtrToIntOp::create(rewriter, op.getLoc(), llvmDstTy,
2376 adaptor.getLhs());
2377 auto rhs = mlir::LLVM::PtrToIntOp::create(rewriter, op.getLoc(), llvmDstTy,
2378 adaptor.getRhs());
2379
2380 auto diff =
2381 mlir::LLVM::SubOp::create(rewriter, op.getLoc(), llvmDstTy, lhs, rhs);
2382
2383 cir::PointerType ptrTy = op.getLhs().getType();
2385 uint64_t typeSize = getTypeSize(ptrTy.getPointee(), *op);
2386
2387 // Avoid silly division by 1.
2388 mlir::Value resultVal = diff.getResult();
2389 if (typeSize != 1) {
2390 auto typeSizeVal = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
2391 llvmDstTy, typeSize);
2392
2393 if (dstTy.isUnsigned()) {
2394 auto uDiv =
2395 mlir::LLVM::UDivOp::create(rewriter, op.getLoc(), diff, typeSizeVal);
2396 uDiv.setIsExact(true);
2397 resultVal = uDiv.getResult();
2398 } else {
2399 auto sDiv =
2400 mlir::LLVM::SDivOp::create(rewriter, op.getLoc(), diff, typeSizeVal);
2401 sDiv.setIsExact(true);
2402 resultVal = sDiv.getResult();
2403 }
2404 }
2405 rewriter.replaceOp(op, resultVal);
2406 return mlir::success();
2407}
2408
2409mlir::LogicalResult CIRToLLVMExpectOpLowering::matchAndRewrite(
2410 cir::ExpectOp op, OpAdaptor adaptor,
2411 mlir::ConversionPatternRewriter &rewriter) const {
2412 // TODO(cir): do not generate LLVM intrinsics under -O0
2414
2415 std::optional<llvm::APFloat> prob = op.getProb();
2416 if (prob)
2417 rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectWithProbabilityOp>(
2418 op, adaptor.getVal(), adaptor.getExpected(), prob.value());
2419 else
2420 rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectOp>(op, adaptor.getVal(),
2421 adaptor.getExpected());
2422 return mlir::success();
2423}
2424
2425mlir::LogicalResult CIRToLLVMAbsOpLowering::matchAndRewrite(
2426 cir::AbsOp op, OpAdaptor adaptor,
2427 mlir::ConversionPatternRewriter &rewriter) const {
2428 mlir::Type resTy = typeConverter->convertType(op.getType());
2429 auto absOp = mlir::LLVM::AbsOp::create(rewriter, op.getLoc(), resTy,
2430 adaptor.getOperands()[0],
2431 adaptor.getMinIsPoison());
2432 rewriter.replaceOp(op, absOp);
2433 return mlir::success();
2434}
2435
2436/// Convert the `cir.func` attributes to `llvm.func` attributes.
2437/// Only retain those attributes that are not constructed by
2438/// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out
2439/// argument attributes.
2440void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
2441 cir::FuncOp func, bool filterArgAndResAttrs,
2442 SmallVectorImpl<mlir::NamedAttribute> &result) const {
2443 for (mlir::NamedAttribute attr : func->getAttrs()) {
2444 if (attr.getName() == mlir::SymbolTable::getSymbolAttrName() ||
2445 attr.getName() == func.getFunctionTypeAttrName() ||
2446 attr.getName() == getLinkageAttrNameString() ||
2447 attr.getName() == func.getCallingConvAttrName() ||
2448 attr.getName() == func.getDsoLocalAttrName() ||
2449 attr.getName() == func.getInlineKindAttrName() ||
2450 attr.getName() == func.getSideEffectAttrName() ||
2451 attr.getName() == CIRDialect::getNoReturnAttrName() ||
2452 attr.getName() == func.getAnnotationsAttrName() ||
2453 (filterArgAndResAttrs &&
2454 (attr.getName() == func.getArgAttrsAttrName() ||
2455 attr.getName() == func.getResAttrsAttrName())))
2456 continue;
2457
2459 if (attr.getName() == func.getArgAttrsAttrName()) {
2460 auto argAttrs = cast<mlir::ArrayAttr>(attr.getValue());
2461 result.emplace_back(
2462 attr.getName(),
2463 convertTypedArgAttrs(argAttrs, *getTypeConverter(), getContext()));
2464 continue;
2465 }
2466 result.push_back(attr);
2467 }
2468}
2469
2470mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
2471 cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty, OpAdaptor adaptor,
2472 mlir::ConversionPatternRewriter &rewriter) const {
2473 SmallVector<mlir::NamedAttribute, 4> attributes;
2474 lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
2475
2476 mlir::Location loc = op.getLoc();
2477 auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
2478 op, ty, convertLinkage(op.getLinkage()), op.getName(), op.getDsoLocal(),
2479 /*threadLocal=*/false, attributes);
2480
2481 // Create the alias body
2482 mlir::OpBuilder builder(op.getContext());
2483 mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
2484 builder.setInsertionPointToStart(block);
2485 // The type of AddressOfOp is always a pointer.
2487 mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get(ty.getContext());
2488 auto addrOp = mlir::LLVM::AddressOfOp::create(builder, loc, ptrTy, aliasee);
2489 mlir::LLVM::ReturnOp::create(builder, loc, addrOp);
2490
2491 return mlir::success();
2492}
2493
2494mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
2495 cir::FuncOp op, OpAdaptor adaptor,
2496 mlir::ConversionPatternRewriter &rewriter) const {
2497
2498 cir::FuncType fnType = op.getFunctionType();
2499 bool isDsoLocal = op.getDsoLocal();
2500 mlir::TypeConverter::SignatureConversion signatureConversion(
2501 fnType.getNumInputs());
2502
2503 for (const auto &argType : llvm::enumerate(fnType.getInputs())) {
2504 mlir::Type convertedType = typeConverter->convertType(argType.value());
2505 if (!convertedType)
2506 return mlir::failure();
2507 signatureConversion.addInputs(argType.index(), convertedType);
2508 }
2509
2510 mlir::Type resultType =
2511 getTypeConverter()->convertType(fnType.getReturnType());
2512
2513 // Create the LLVM function operation.
2514 mlir::Type llvmFnTy = mlir::LLVM::LLVMFunctionType::get(
2515 resultType ? resultType : mlir::LLVM::LLVMVoidType::get(getContext()),
2516 signatureConversion.getConvertedTypes(),
2517 /*isVarArg=*/fnType.isVarArg());
2518
2519 // If this is an alias, it needs to be lowered to llvm::AliasOp.
2520 if (std::optional<llvm::StringRef> aliasee = op.getAliasee())
2521 return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
2522
2523 // LLVMFuncOp expects a single FileLine Location instead of a fused
2524 // location.
2525 mlir::Location loc = op.getLoc();
2526 if (mlir::FusedLoc fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(loc))
2527 loc = fusedLoc.getLocations()[0];
2528 assert((mlir::isa<mlir::FileLineColLoc>(loc) ||
2529 mlir::isa<mlir::UnknownLoc>(loc)) &&
2530 "expected single location or unknown location here");
2531
2532 mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
2533 mlir::LLVM::CConv cconv = convertCallingConv(op.getCallingConv());
2534 SmallVector<mlir::NamedAttribute, 4> attributes;
2535 lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
2536
2537 mlir::LLVM::LLVMFuncOp fn = mlir::LLVM::LLVMFuncOp::create(
2538 rewriter, loc, op.getName(), llvmFnTy, linkage, isDsoLocal, cconv,
2539 mlir::SymbolRefAttr(), attributes);
2540
2542
2543 if (std::optional<cir::SideEffect> sideEffectKind = op.getSideEffect()) {
2544 switch (*sideEffectKind) {
2545 case cir::SideEffect::All:
2546 break;
2547 case cir::SideEffect::Pure:
2548 fn.setMemoryEffectsAttr(mlir::LLVM::MemoryEffectsAttr::get(
2549 fn.getContext(),
2550 /*other=*/mlir::LLVM::ModRefInfo::Ref,
2551 /*argMem=*/mlir::LLVM::ModRefInfo::Ref,
2552 /*inaccessibleMem=*/mlir::LLVM::ModRefInfo::Ref,
2553 /*errnoMem=*/mlir::LLVM::ModRefInfo::Ref,
2554 /*targetMem0=*/mlir::LLVM::ModRefInfo::Ref,
2555 /*targetMem1=*/mlir::LLVM::ModRefInfo::Ref));
2556 fn.setNoUnwind(true);
2557 fn.setWillReturn(true);
2558 break;
2559 case cir::SideEffect::Const:
2560 fn.setMemoryEffectsAttr(mlir::LLVM::MemoryEffectsAttr::get(
2561 fn.getContext(),
2562 /*other=*/mlir::LLVM::ModRefInfo::NoModRef,
2563 /*argMem=*/mlir::LLVM::ModRefInfo::NoModRef,
2564 /*inaccessibleMem=*/mlir::LLVM::ModRefInfo::NoModRef,
2565 /*errnoMem=*/mlir::LLVM::ModRefInfo::NoModRef,
2566 /*targetMem0=*/mlir::LLVM::ModRefInfo::NoModRef,
2567 /*targetMem1=*/mlir::LLVM::ModRefInfo::NoModRef));
2568 fn.setNoUnwind(true);
2569 fn.setWillReturn(true);
2570 break;
2571 }
2572 }
2573
2574 if (op->hasAttr(CIRDialect::getNoReturnAttrName()))
2575 fn.setNoreturn(true);
2576
2577 if (std::optional<cir::InlineKind> inlineKind = op.getInlineKind()) {
2578 fn.setNoInline(*inlineKind == cir::InlineKind::NoInline);
2579 fn.setInlineHint(*inlineKind == cir::InlineKind::InlineHint);
2580 fn.setAlwaysInline(*inlineKind == cir::InlineKind::AlwaysInline);
2581 }
2582
2583 if (std::optional<llvm::StringRef> personality = op.getPersonality())
2584 fn.setPersonality(*personality);
2585
2586 fn.setVisibility_(
2587 lowerCIRVisibilityToLLVMVisibility(op.getGlobalVisibility()));
2588
2589 rewriter.inlineRegionBefore(op.getBody(), fn.getBody(), fn.end());
2590 if (failed(rewriter.convertRegionTypes(&fn.getBody(), *typeConverter,
2591 &signatureConversion)))
2592 return mlir::failure();
2593
2594 rewriter.eraseOp(op);
2595
2596 return mlir::LogicalResult::success();
2597}
2598
2599mlir::LogicalResult CIRToLLVMGetGlobalOpLowering::matchAndRewrite(
2600 cir::GetGlobalOp op, OpAdaptor adaptor,
2601 mlir::ConversionPatternRewriter &rewriter) const {
2602 // FIXME(cir): Premature DCE to avoid lowering stuff we're not using.
2603 // CIRGen should mitigate this and not emit the get_global.
2604 if (op->getUses().empty()) {
2605 rewriter.eraseOp(op);
2606 return mlir::success();
2607 }
2608
2609 mlir::Type type = getTypeConverter()->convertType(op.getType());
2610 mlir::Operation *newop = mlir::LLVM::AddressOfOp::create(
2611 rewriter, op.getLoc(), type, op.getName());
2612
2613 if (op.getTls()) {
2614 // Handle access to TLS via intrinsic.
2615 newop = mlir::LLVM::ThreadlocalAddressOp::create(rewriter, op.getLoc(),
2616 type, newop->getResult(0));
2617 }
2618
2619 rewriter.replaceOp(op, newop);
2620 return mlir::success();
2621}
2622
2623llvm::SmallVector<mlir::NamedAttribute>
2624CIRToLLVMGlobalOpLowering::lowerGlobalAttributes(
2625 cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const {
2626 SmallVector<mlir::NamedAttribute> attributes;
2627
2628 if (mlir::StringAttr sectionAttr = op.getSectionAttr())
2629 attributes.push_back(rewriter.getNamedAttr("section", sectionAttr));
2630
2631 mlir::LLVM::VisibilityAttr visibility = mlir::LLVM::VisibilityAttr::get(
2632 getContext(),
2633 lowerCIRVisibilityToLLVMVisibility(op.getGlobalVisibility()));
2634 attributes.push_back(rewriter.getNamedAttr("visibility_", visibility));
2635
2636 if (op->getAttr(CUDAExternallyInitializedAttr::getMnemonic()))
2637 attributes.push_back(rewriter.getNamedAttr("externally_initialized",
2638 rewriter.getUnitAttr()));
2639
2640 return attributes;
2641}
2642
2643/// Replace CIR global with a region initialized LLVM global and update
2644/// insertion point to the end of the initializer block.
2645void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp(
2646 cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const {
2647 mlir::Type llvmType =
2648 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getSymType());
2649
2650 // Keep the global's type in sync with the value built by CIRAttrToValue: a
2651 // flexible array member initializer requires an oversized anonymous struct.
2652 if (std::optional<mlir::Attribute> init = op.getInitialValue())
2653 llvmType = adjustGlobalTypeForInit(llvmType, *init, *getTypeConverter(),
2654 dataLayout);
2655
2656 // FIXME: These default values are placeholders until the the equivalent
2657 // attributes are available on cir.global ops. This duplicates code
2658 // in CIRToLLVMGlobalOpLowering::matchAndRewrite() but that will go
2659 // away when the placeholders are no longer needed.
2660 const bool isConst = op.getConstant();
2661 unsigned addrSpace = 0;
2662 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
2663 op.getAddrSpaceAttr()))
2664 addrSpace = targetAS.getValue();
2665 const bool isDsoLocal = op.getDsoLocal();
2666 const bool isThreadLocal = (bool)op.getTlsModelAttr();
2667 const uint64_t alignment = op.getAlignment().value_or(0);
2668 const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
2669 const StringRef symbol = op.getSymName();
2670 mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
2671
2672 SmallVector<mlir::NamedAttribute> attributes =
2673 lowerGlobalAttributes(op, rewriter);
2674
2675 mlir::LLVM::GlobalOp newGlobalOp =
2676 rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
2677 op, llvmType, isConst, linkage, symbol, nullptr, alignment, addrSpace,
2678 isDsoLocal, isThreadLocal, comdatAttr, attributes);
2679 newGlobalOp.getRegion().emplaceBlock();
2680 rewriter.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());
2681}
2682
2683mlir::LogicalResult
2684CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
2685 cir::GlobalOp op, mlir::Attribute init,
2686 mlir::ConversionPatternRewriter &rewriter) const {
2687 // TODO: Generalize this handling when more types are needed here.
2688 assert((isa<cir::BlockAddrInfoAttr, cir::ConstArrayAttr, cir::ConstRecordAttr,
2689 cir::ConstVectorAttr, cir::ConstPtrAttr, cir::ConstComplexAttr,
2690 cir::GlobalViewAttr, cir::TypeInfoAttr, cir::UndefAttr,
2691 cir::PoisonAttr, cir::VTableAttr, cir::ZeroAttr>(init)));
2692
2693 // TODO(cir): once LLVM's dialect has proper equivalent attributes this
2694 // should be updated. For now, we use a custom op to initialize globals
2695 // to the appropriate value.
2696 const mlir::Location loc = op.getLoc();
2697 setupRegionInitializedLLVMGlobalOp(op, rewriter);
2698
2699 // Pass blockInfoAddr so that block address initializers (either as the whole
2700 // initializer or nested inside an aggregate) can be resolved by the
2701 // BlockAddrInfoAttr visitor.
2702 CIRAttrToValue valueConverter(op, rewriter, typeConverter, &blockInfoAddr);
2703 mlir::Value value = valueConverter.visit(init);
2704 mlir::LLVM::ReturnOp::create(rewriter, loc, value);
2705 return mlir::success();
2706}
2707
2708mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
2709 cir::GlobalOp op, OpAdaptor adaptor,
2710 mlir::ConversionPatternRewriter &rewriter) const {
2711 // If this global requires non-trivial initialization or destruction,
2712 // that needs to be moved to runtime handlers during LoweringPrepare.
2713 if (!op.getCtorRegion().empty() || !op.getDtorRegion().empty())
2714 return op.emitError() << "GlobalOp ctor and dtor regions should be removed "
2715 "in LoweringPrepare";
2716
2717 std::optional<mlir::Attribute> init = op.getInitialValue();
2718
2719 // Fetch required values to create LLVM op.
2720 const mlir::Type cirSymType = op.getSymType();
2721
2722 // This is the LLVM dialect type.
2723 mlir::Type llvmType =
2724 convertTypeForMemory(*getTypeConverter(), dataLayout, cirSymType);
2725 if (!llvmType)
2726 return op.emitError()
2727 << "NYI: lowering global of a type with no memory representation";
2728
2729 // A flexible array member initializer makes the constant larger than the
2730 // record's declared type, so the global must use an oversized anonymous
2731 // struct instead.
2732 if (init.has_value())
2733 llvmType = adjustGlobalTypeForInit(llvmType, *init, *getTypeConverter(),
2734 dataLayout);
2735
2736 // FIXME: These default values are placeholders until the the equivalent
2737 // attributes are available on cir.global ops.
2738 const bool isConst = op.getConstant();
2739 unsigned addrSpace = 0;
2740 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
2741 op.getAddrSpaceAttr()))
2742 addrSpace = targetAS.getValue();
2743 const bool isDsoLocal = op.getDsoLocal();
2744 const bool isThreadLocal = (bool)op.getTlsModelAttr();
2745 const uint64_t alignment = op.getAlignment().value_or(0);
2746 const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
2747 const StringRef symbol = op.getSymName();
2748 SmallVector<mlir::NamedAttribute> attributes =
2749 lowerGlobalAttributes(op, rewriter);
2750
2751 // If this is a variable alias, lower it to llvm.mlir.alias.
2752 if (std::optional<llvm::StringRef> aliasee = op.getAliasee()) {
2753 mlir::Location loc = op.getLoc();
2754 auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
2755 op, llvmType, linkage, symbol, isDsoLocal, isThreadLocal, attributes);
2756
2757 mlir::OpBuilder builder(op.getContext());
2758 mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
2759 builder.setInsertionPointToStart(block);
2760 mlir::Type ptrTy =
2761 mlir::LLVM::LLVMPointerType::get(getContext(), addrSpace);
2762 auto addrOp =
2763 mlir::LLVM::AddressOfOp::create(builder, loc, ptrTy, *aliasee);
2764 mlir::LLVM::ReturnOp::create(builder, loc, addrOp);
2765 return mlir::success();
2766 }
2767
2768 if (init.has_value()) {
2769 if (mlir::isa<cir::FPAttr, cir::IntAttr, cir::BoolAttr>(init.value())) {
2770 GlobalInitAttrRewriter initRewriter(llvmType, rewriter);
2771 init = initRewriter.visit(init.value());
2772 // If initRewriter returned a null attribute, init will have a value but
2773 // the value will be null. If that happens, initRewriter didn't handle the
2774 // attribute type. It probably needs to be added to
2775 // GlobalInitAttrRewriter.
2776 if (!init.value()) {
2777 op.emitError() << "unsupported initializer '" << init.value() << "'";
2778 return mlir::failure();
2779 }
2780 } else if (auto constArr =
2781 mlir::dyn_cast<cir::ConstArrayAttr>(init.value())) {
2782 // Bulk-emit llvm.mlir.global when lowerConstArrayAttr can build the
2783 // whole initializer as one aggregate attribute (no insertvalue
2784 // region). Leaf type must match what lowerConstArrayAttr handles
2785 // (pointers, integers, bools, floats, and string literals with
2786 // trailing_zeros).
2788 getConstArrayBaseElementType(constArr.getType()))) {
2789 mlir::ModuleOp modOp = op->getParentOfType<mlir::ModuleOp>();
2790 if (std::optional<mlir::Attribute> bulkInit =
2791 lowerConstArrayAttr(constArr, typeConverter, modOp)) {
2792 mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
2793 rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
2794 op, llvmType, isConst, linkage, symbol, bulkInit.value(),
2795 alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr,
2796 attributes);
2797 return mlir::success();
2798 }
2799 }
2800 return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
2801 } else if (auto constRecord =
2802 mlir::dyn_cast<cir::ConstRecordAttr>(init.value())) {
2803 // Bulk-emit llvm.mlir.global when every member of the record can be
2804 // lowered to a constant attribute. The LLVM dialect global translation
2805 // turns an ArrayAttr (one element per struct field) into an
2806 // llvm::ConstantStruct, so the whole initializer becomes a single
2807 // attribute on the global instead of an insertvalue region.
2808 mlir::ModuleOp modOp = op->getParentOfType<mlir::ModuleOp>();
2809 if (std::optional<mlir::Attribute> bulkInit =
2810 lowerConstRecordAttr(constRecord, typeConverter, modOp)) {
2811 mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
2812 rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
2813 op, llvmType, isConst, linkage, symbol, bulkInit.value(), alignment,
2814 addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes);
2815 return mlir::success();
2816 }
2817 return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
2818 } else if (mlir::isa<cir::BlockAddrInfoAttr, cir::ConstVectorAttr,
2819 cir::ConstRecordAttr, cir::ConstPtrAttr,
2820 cir::ConstComplexAttr, cir::GlobalViewAttr,
2821 cir::TypeInfoAttr, cir::UndefAttr, cir::PoisonAttr,
2822 cir::VTableAttr, cir::ZeroAttr>(init.value())) {
2823 // TODO(cir): once LLVM's dialect has proper equivalent attributes this
2824 // should be updated. For now, we use a custom op to initialize globals
2825 // to the appropriate value.
2826 return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
2827 } else {
2828 // We will only get here if new initializer types are added and this
2829 // code is not updated to handle them.
2830 op.emitError() << "unsupported initializer '" << init.value() << "'";
2831 return mlir::failure();
2832 }
2833 }
2834
2835 mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
2836 rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
2837 op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()),
2838 alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes);
2839
2840 return mlir::success();
2841}
2842
2843mlir::SymbolRefAttr
2844CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
2845 mlir::OpBuilder &builder) const {
2846 if (!op.getComdat())
2847 return mlir::SymbolRefAttr{};
2848
2849 mlir::ModuleOp modOp = op->getParentOfType<mlir::ModuleOp>();
2850 mlir::OpBuilder::InsertionGuard guard(builder);
2851 StringRef comdatName("__llvm_comdat_globals");
2852 if (!comdatOp) {
2853 builder.setInsertionPointToStart(modOp.getBody());
2854 comdatOp =
2855 mlir::LLVM::ComdatOp::create(builder, modOp.getLoc(), comdatName);
2856 }
2857
2858 if (auto comdatSelector = comdatOp.lookupSymbol<mlir::LLVM::ComdatSelectorOp>(
2859 op.getSymName())) {
2860 return mlir::SymbolRefAttr::get(
2861 builder.getContext(), comdatName,
2862 mlir::FlatSymbolRefAttr::get(comdatSelector.getSymNameAttr()));
2863 }
2864
2865 builder.setInsertionPointToStart(&comdatOp.getBody().back());
2866 auto selectorOp = mlir::LLVM::ComdatSelectorOp::create(
2867 builder, comdatOp.getLoc(), op.getSymName(),
2868 mlir::LLVM::comdat::Comdat::Any);
2869 return mlir::SymbolRefAttr::get(
2870 builder.getContext(), comdatName,
2871 mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr()));
2872}
2873
2874mlir::LogicalResult CIRToLLVMSwitchFlatOpLowering::matchAndRewrite(
2875 cir::SwitchFlatOp op, OpAdaptor adaptor,
2876 mlir::ConversionPatternRewriter &rewriter) const {
2877
2878 llvm::SmallVector<mlir::APInt, 8> caseValues;
2879 for (mlir::Attribute val : op.getCaseValues()) {
2880 auto intAttr = cast<cir::IntAttr>(val);
2881 caseValues.push_back(intAttr.getValue());
2882 }
2883
2884 llvm::SmallVector<mlir::Block *, 8> caseDestinations;
2885 llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
2886
2887 for (mlir::Block *x : op.getCaseDestinations())
2888 caseDestinations.push_back(x);
2889
2890 for (mlir::OperandRange x : op.getCaseOperands())
2891 caseOperands.push_back(x);
2892
2893 // Set switch op to branch to the newly created blocks.
2894 rewriter.setInsertionPoint(op);
2895 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
2896 op, adaptor.getCondition(), op.getDefaultDestination(),
2897 op.getDefaultOperands(), caseValues, caseDestinations, caseOperands);
2898 return mlir::success();
2899}
2900
2901static mlir::LLVM::IntegerOverflowFlags nswFlag(bool nsw) {
2902 return nsw ? mlir::LLVM::IntegerOverflowFlags::nsw
2903 : mlir::LLVM::IntegerOverflowFlags::none;
2904}
2905
2906template <typename CIROp, typename LLVMIntOp>
2907static mlir::LogicalResult
2908lowerIncDecOp(CIROp op, typename CIROp::Adaptor adaptor,
2909 mlir::ConversionPatternRewriter &rewriter) {
2910 mlir::Type llvmType = adaptor.getInput().getType();
2911 mlir::Location loc = op.getLoc();
2912
2913 auto maybeNSW = nswFlag(op.getNoSignedWrap());
2914 mlir::LLVM::ConstantOp one;
2915 if (mlir::isa<cir::VectorType>(op.getType())) {
2916 mlir::DenseIntElementsAttr oneVec = mlir::DenseIntElementsAttr::get(
2917 mlir::cast<mlir::ShapedType>(llvmType), 1);
2918 one = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, oneVec);
2919 } else {
2920 one = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, 1);
2921 }
2922 rewriter.replaceOpWithNewOp<LLVMIntOp>(op, adaptor.getInput(), one, maybeNSW);
2923 return mlir::success();
2924}
2925
2926mlir::LogicalResult CIRToLLVMIncOpLowering::matchAndRewrite(
2927 cir::IncOp op, OpAdaptor adaptor,
2928 mlir::ConversionPatternRewriter &rewriter) const {
2929 return lowerIncDecOp<cir::IncOp, mlir::LLVM::AddOp>(op, adaptor, rewriter);
2930}
2931
2932mlir::LogicalResult CIRToLLVMDecOpLowering::matchAndRewrite(
2933 cir::DecOp op, OpAdaptor adaptor,
2934 mlir::ConversionPatternRewriter &rewriter) const {
2935 return lowerIncDecOp<cir::DecOp, mlir::LLVM::SubOp>(op, adaptor, rewriter);
2936}
2937
2938mlir::LogicalResult CIRToLLVMMinusOpLowering::matchAndRewrite(
2939 cir::MinusOp op, OpAdaptor adaptor,
2940 mlir::ConversionPatternRewriter &rewriter) const {
2941 bool isVector = mlir::isa<cir::VectorType>(op.getType());
2942 mlir::Type llvmType = adaptor.getInput().getType();
2943 mlir::Location loc = op.getLoc();
2944
2945 auto maybeNSW = nswFlag(op.getNoSignedWrap());
2946 mlir::Value zero;
2947 if (isVector)
2948 zero = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmType);
2949 else
2950 zero = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, 0);
2951 rewriter.replaceOpWithNewOp<mlir::LLVM::SubOp>(op, zero, adaptor.getInput(),
2952 maybeNSW);
2953 return mlir::success();
2954}
2955
2956mlir::LogicalResult CIRToLLVMNotOpLowering::matchAndRewrite(
2957 cir::NotOp op, OpAdaptor adaptor,
2958 mlir::ConversionPatternRewriter &rewriter) const {
2959 mlir::Type elementType = elementTypeIfVector(op.getType());
2960 bool isVector = mlir::isa<cir::VectorType>(op.getType());
2961 mlir::Type llvmType = adaptor.getInput().getType();
2962 mlir::Location loc = op.getLoc();
2963
2964 if (mlir::isa<cir::IntType>(elementType)) {
2965 mlir::Value minusOne;
2966 if (isVector) {
2967 const uint64_t numElements =
2968 mlir::dyn_cast<cir::VectorType>(op.getType()).getSize();
2969 SmallVector<int32_t> values(numElements, -1);
2970 mlir::DenseIntElementsAttr denseVec = rewriter.getI32VectorAttr(values);
2971 minusOne =
2972 mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, denseVec);
2973 } else {
2974 minusOne = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, -1);
2975 }
2976 rewriter.replaceOpWithNewOp<mlir::LLVM::XOrOp>(op, adaptor.getInput(),
2977 minusOne);
2978 return mlir::success();
2979 }
2980 if (mlir::isa<cir::BoolType>(elementType)) {
2981 auto one = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, 1);
2982 rewriter.replaceOpWithNewOp<mlir::LLVM::XOrOp>(op, adaptor.getInput(), one);
2983 return mlir::success();
2984 }
2985 return op.emitError() << "Unsupported type for bitwise NOT";
2986}
2987
2988static bool isIntTypeUnsigned(mlir::Type type) {
2989 // TODO: Ideally, we should only need to check cir::IntType here.
2990 return mlir::isa<cir::IntType>(type)
2991 ? mlir::cast<cir::IntType>(type).isUnsigned()
2992 : mlir::cast<mlir::IntegerType>(type).isUnsigned();
2993}
2994
2995//===----------------------------------------------------------------------===//
2996// Binary Op Lowering
2997//===----------------------------------------------------------------------===//
2998
2999template <typename BinOp>
3000static mlir::LLVM::IntegerOverflowFlags intOverflowFlag(BinOp op) {
3001 if (op.getNoUnsignedWrap())
3002 return mlir::LLVM::IntegerOverflowFlags::nuw;
3003 if (op.getNoSignedWrap())
3004 return mlir::LLVM::IntegerOverflowFlags::nsw;
3005 return mlir::LLVM::IntegerOverflowFlags::none;
3006}
3007
3008/// Lower an arithmetic op that supports saturation, overflow flags, and an FP
3009/// Lower an integer Add/Sub op that may use saturating-arithmetic semantics.
3010template <typename UIntSatOp, typename SIntSatOp, typename IntOp,
3011 typename CIROp>
3012static mlir::LogicalResult
3013lowerSaturatableArithOp(CIROp op, mlir::Value lhs, mlir::Value rhs,
3014 mlir::ConversionPatternRewriter &rewriter) {
3015 const mlir::Type eltType = elementTypeIfVector(op.getRhs().getType());
3016 assert(cir::isIntOrBoolType(eltType) &&
3017 "saturatable arith op expects integer operand types");
3018 if (op.getSaturated()) {
3019 if (isIntTypeUnsigned(eltType))
3020 rewriter.replaceOpWithNewOp<UIntSatOp>(op, lhs, rhs);
3021 else
3022 rewriter.replaceOpWithNewOp<SIntSatOp>(op, lhs, rhs);
3023 return mlir::success();
3024 }
3025 rewriter.replaceOpWithNewOp<IntOp>(op, lhs, rhs, intOverflowFlag(op));
3026 return mlir::success();
3027}
3028
3029mlir::LogicalResult CIRToLLVMAddOpLowering::matchAndRewrite(
3030 cir::AddOp op, OpAdaptor adaptor,
3031 mlir::ConversionPatternRewriter &rewriter) const {
3032 return lowerSaturatableArithOp<mlir::LLVM::UAddSat, mlir::LLVM::SAddSat,
3033 mlir::LLVM::AddOp>(op, adaptor.getLhs(),
3034 adaptor.getRhs(), rewriter);
3035}
3036
3037mlir::LogicalResult CIRToLLVMSubOpLowering::matchAndRewrite(
3038 cir::SubOp op, OpAdaptor adaptor,
3039 mlir::ConversionPatternRewriter &rewriter) const {
3040 return lowerSaturatableArithOp<mlir::LLVM::USubSat, mlir::LLVM::SSubSat,
3041 mlir::LLVM::SubOp>(op, adaptor.getLhs(),
3042 adaptor.getRhs(), rewriter);
3043}
3044
3045mlir::LogicalResult CIRToLLVMMulOpLowering::matchAndRewrite(
3046 cir::MulOp op, OpAdaptor adaptor,
3047 mlir::ConversionPatternRewriter &rewriter) const {
3048 assert(cir::isIntOrBoolType(elementTypeIfVector(op.getRhs().getType())) &&
3049 "cir.mul expects integer operand types");
3050 rewriter.replaceOpWithNewOp<mlir::LLVM::MulOp>(
3051 op, adaptor.getLhs(), adaptor.getRhs(), intOverflowFlag(op));
3052 return mlir::success();
3053}
3054
3055/// Lower an integer Div/Rem op to its signed or unsigned LLVM counterpart.
3056template <typename UIntOp, typename SIntOp, typename CIROp>
3057static mlir::LogicalResult
3058lowerIntBinaryOp(CIROp op, mlir::Value lhs, mlir::Value rhs,
3059 mlir::ConversionPatternRewriter &rewriter) {
3060 const mlir::Type eltType = elementTypeIfVector(op.getRhs().getType());
3061 assert(cir::isIntOrBoolType(eltType) &&
3062 "integer binary op expects integer operand types");
3063 if (isIntTypeUnsigned(eltType))
3064 rewriter.replaceOpWithNewOp<UIntOp>(op, lhs, rhs);
3065 else
3066 rewriter.replaceOpWithNewOp<SIntOp>(op, lhs, rhs);
3067 return mlir::success();
3068}
3069
3070mlir::LogicalResult CIRToLLVMDivOpLowering::matchAndRewrite(
3071 cir::DivOp op, OpAdaptor adaptor,
3072 mlir::ConversionPatternRewriter &rewriter) const {
3074 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
3075}
3076
3077mlir::LogicalResult CIRToLLVMRemOpLowering::matchAndRewrite(
3078 cir::RemOp op, OpAdaptor adaptor,
3079 mlir::ConversionPatternRewriter &rewriter) const {
3081 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
3082}
3083
3084template <typename CIROp, typename UIntOp, typename SIntOp>
3085static mlir::LogicalResult
3086lowerMinMaxOp(CIROp op, typename CIROp::Adaptor adaptor,
3087 mlir::ConversionPatternRewriter &rewriter) {
3088 const mlir::Value lhs = adaptor.getLhs();
3089 const mlir::Value rhs = adaptor.getRhs();
3090 if (isIntTypeUnsigned(elementTypeIfVector(op.getRhs().getType())))
3091 rewriter.replaceOpWithNewOp<UIntOp>(op, lhs, rhs);
3092 else
3093 rewriter.replaceOpWithNewOp<SIntOp>(op, lhs, rhs);
3094 return mlir::success();
3095}
3096
3097mlir::LogicalResult CIRToLLVMMaxOpLowering::matchAndRewrite(
3098 cir::MaxOp op, OpAdaptor adaptor,
3099 mlir::ConversionPatternRewriter &rewriter) const {
3101 op, adaptor, rewriter);
3102}
3103
3104mlir::LogicalResult CIRToLLVMMinOpLowering::matchAndRewrite(
3105 cir::MinOp op, OpAdaptor adaptor,
3106 mlir::ConversionPatternRewriter &rewriter) const {
3108 op, adaptor, rewriter);
3109}
3110
3111/// Convert from a CIR comparison kind to an LLVM IR integral comparison kind.
3112static mlir::LLVM::ICmpPredicate
3113convertCmpKindToICmpPredicate(cir::CmpOpKind kind, bool isSigned) {
3114 using CIR = cir::CmpOpKind;
3115 using LLVMICmp = mlir::LLVM::ICmpPredicate;
3116 switch (kind) {
3117 case CIR::eq:
3118 return LLVMICmp::eq;
3119 case CIR::ne:
3120 return LLVMICmp::ne;
3121 case CIR::lt:
3122 return (isSigned ? LLVMICmp::slt : LLVMICmp::ult);
3123 case CIR::le:
3124 return (isSigned ? LLVMICmp::sle : LLVMICmp::ule);
3125 case CIR::gt:
3126 return (isSigned ? LLVMICmp::sgt : LLVMICmp::ugt);
3127 case CIR::ge:
3128 return (isSigned ? LLVMICmp::sge : LLVMICmp::uge);
3129 case CIR::one:
3130 case CIR::uno:
3131 llvm_unreachable("FP-only comparison used with integer type");
3132 }
3133 llvm_unreachable("Unknown CmpOpKind");
3134}
3135
3136/// Convert from a CIR comparison kind to an LLVM IR floating-point comparison
3137/// kind.
3138static mlir::LLVM::FCmpPredicate
3140 using CIR = cir::CmpOpKind;
3141 using LLVMFCmp = mlir::LLVM::FCmpPredicate;
3142 switch (kind) {
3143 case CIR::eq:
3144 return LLVMFCmp::oeq;
3145 case CIR::ne:
3146 return LLVMFCmp::une;
3147 case CIR::lt:
3148 return LLVMFCmp::olt;
3149 case CIR::le:
3150 return LLVMFCmp::ole;
3151 case CIR::gt:
3152 return LLVMFCmp::ogt;
3153 case CIR::ge:
3154 return LLVMFCmp::oge;
3155 case CIR::one:
3156 return LLVMFCmp::one;
3157 case CIR::uno:
3158 return LLVMFCmp::uno;
3159 }
3160 llvm_unreachable("Unknown CmpOpKind");
3161}
3162
3163mlir::LogicalResult CIRToLLVMCmpOpLowering::matchAndRewrite(
3164 cir::CmpOp cmpOp, OpAdaptor adaptor,
3165 mlir::ConversionPatternRewriter &rewriter) const {
3166 mlir::Type type = cmpOp.getLhs().getType();
3167
3168 if (mlir::isa<cir::IntType, mlir::IntegerType>(type)) {
3169 bool isSigned = mlir::isa<cir::IntType>(type)
3170 ? mlir::cast<cir::IntType>(type).isSigned()
3171 : mlir::cast<mlir::IntegerType>(type).isSigned();
3172 mlir::LLVM::ICmpPredicate kind =
3173 convertCmpKindToICmpPredicate(cmpOp.getKind(), isSigned);
3174 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
3175 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
3176 return mlir::success();
3177 }
3178
3179 if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(type)) {
3180 mlir::LLVM::ICmpPredicate kind =
3181 convertCmpKindToICmpPredicate(cmpOp.getKind(),
3182 /* isSigned=*/false);
3183 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
3184 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
3185 return mlir::success();
3186 }
3187
3188 if (auto vptrTy = mlir::dyn_cast<cir::VPtrType>(type)) {
3189 // !cir.vptr is a special case, but it's just a pointer to LLVM.
3190 auto kind = convertCmpKindToICmpPredicate(cmpOp.getKind(),
3191 /* isSigned=*/false);
3192 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
3193 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
3194 return mlir::success();
3195 }
3196
3197 if (mlir::isa<cir::FPTypeInterface>(type)) {
3198 mlir::LLVM::FCmpPredicate kind =
3199 convertCmpKindToFCmpPredicate(cmpOp.getKind());
3200 rewriter.replaceOpWithNewOp<mlir::LLVM::FCmpOp>(
3201 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
3202 return mlir::success();
3203 }
3204
3205 if (mlir::isa<cir::ComplexType>(type)) {
3206 mlir::Value lhs = adaptor.getLhs();
3207 mlir::Value rhs = adaptor.getRhs();
3208 mlir::Location loc = cmpOp.getLoc();
3209
3210 auto complexType = mlir::cast<cir::ComplexType>(cmpOp.getLhs().getType());
3211 mlir::Type complexElemTy =
3212 getTypeConverter()->convertType(complexType.getElementType());
3213
3214 auto lhsReal = mlir::LLVM::ExtractValueOp::create(
3215 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{0}));
3216 auto lhsImag = mlir::LLVM::ExtractValueOp::create(
3217 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{1}));
3218 auto rhsReal = mlir::LLVM::ExtractValueOp::create(
3219 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{0}));
3220 auto rhsImag = mlir::LLVM::ExtractValueOp::create(
3221 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{1}));
3222
3223 if (cmpOp.getKind() == cir::CmpOpKind::eq) {
3224 if (complexElemTy.isInteger()) {
3225 auto realCmp = mlir::LLVM::ICmpOp::create(
3226 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, lhsReal, rhsReal);
3227 auto imagCmp = mlir::LLVM::ICmpOp::create(
3228 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, lhsImag, rhsImag);
3229 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmpOp, realCmp, imagCmp);
3230 return mlir::success();
3231 }
3232
3233 auto realCmp = mlir::LLVM::FCmpOp::create(
3234 rewriter, loc, mlir::LLVM::FCmpPredicate::oeq, lhsReal, rhsReal);
3235 auto imagCmp = mlir::LLVM::FCmpOp::create(
3236 rewriter, loc, mlir::LLVM::FCmpPredicate::oeq, lhsImag, rhsImag);
3237 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmpOp, realCmp, imagCmp);
3238 return mlir::success();
3239 }
3240
3241 if (cmpOp.getKind() == cir::CmpOpKind::ne) {
3242 if (complexElemTy.isInteger()) {
3243 auto realCmp = mlir::LLVM::ICmpOp::create(
3244 rewriter, loc, mlir::LLVM::ICmpPredicate::ne, lhsReal, rhsReal);
3245 auto imagCmp = mlir::LLVM::ICmpOp::create(
3246 rewriter, loc, mlir::LLVM::ICmpPredicate::ne, lhsImag, rhsImag);
3247 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmpOp, realCmp, imagCmp);
3248 return mlir::success();
3249 }
3250
3251 auto realCmp = mlir::LLVM::FCmpOp::create(
3252 rewriter, loc, mlir::LLVM::FCmpPredicate::une, lhsReal, rhsReal);
3253 auto imagCmp = mlir::LLVM::FCmpOp::create(
3254 rewriter, loc, mlir::LLVM::FCmpPredicate::une, lhsImag, rhsImag);
3255 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmpOp, realCmp, imagCmp);
3256 return mlir::success();
3257 }
3258 }
3259
3260 return cmpOp.emitError() << "unsupported type for CmpOp: " << type;
3261}
3262
3263/// Shared lowering logic for checked binary arithmetic overflow operations.
3264/// The \p opStr parameter specifies the arithmetic operation name used in the
3265/// LLVM intrinsic (e.g., "add", "sub", "mul").
3266template <typename OpTy>
3267static mlir::LogicalResult
3268lowerBinOpOverflow(OpTy op, typename OpTy::Adaptor adaptor,
3269 mlir::ConversionPatternRewriter &rewriter,
3270 const mlir::TypeConverter *typeConverter,
3271 llvm::StringRef opStr) {
3272 mlir::Location loc = op.getLoc();
3273 cir::IntType operandTy = op.getLhs().getType();
3274 // The result type may be a `cir.bool`, which behaves as a 1-bit unsigned
3275 // integer for the purposes of the checked arithmetic.
3276 mlir::Type resultTy = op.getResult().getType();
3277 auto resultIntTy = mlir::dyn_cast<cir::IntType>(resultTy);
3278 unsigned resultWidth = resultIntTy ? resultIntTy.getWidth() : 1;
3279 bool resultSigned = resultIntTy && resultIntTy.getIsSigned();
3280
3281 bool sign = operandTy.getIsSigned() || resultSigned;
3282 unsigned width =
3283 std::max(operandTy.getWidth() + (sign && operandTy.isUnsigned()),
3284 resultWidth + (sign && !resultSigned));
3285
3286 mlir::IntegerType encompassedLLVMTy = rewriter.getIntegerType(width);
3287
3288 mlir::Value lhs = adaptor.getLhs();
3289 mlir::Value rhs = adaptor.getRhs();
3290 if (operandTy.getWidth() < width) {
3291 if (operandTy.isSigned()) {
3292 lhs = mlir::LLVM::SExtOp::create(rewriter, loc, encompassedLLVMTy, lhs);
3293 rhs = mlir::LLVM::SExtOp::create(rewriter, loc, encompassedLLVMTy, rhs);
3294 } else {
3295 lhs = mlir::LLVM::ZExtOp::create(rewriter, loc, encompassedLLVMTy, lhs);
3296 rhs = mlir::LLVM::ZExtOp::create(rewriter, loc, encompassedLLVMTy, rhs);
3297 }
3298 }
3299
3300 // The intrinsic name is `@llvm.{s|u}{op}.with.overflow.i{width}`
3301 std::string intrinName = ("llvm." + llvm::Twine(sign ? 's' : 'u') + opStr +
3302 ".with.overflow.i" + llvm::Twine(width))
3303 .str();
3304 auto intrinNameAttr = mlir::StringAttr::get(op.getContext(), intrinName);
3305
3306 mlir::IntegerType overflowLLVMTy = rewriter.getI1Type();
3307 auto intrinRetTy = mlir::LLVM::LLVMStructType::getLiteral(
3308 rewriter.getContext(), {encompassedLLVMTy, overflowLLVMTy});
3309
3310 auto callLLVMIntrinOp = mlir::LLVM::CallIntrinsicOp::create(
3311 rewriter, loc, intrinRetTy, intrinNameAttr, mlir::ValueRange{lhs, rhs});
3312 mlir::Value intrinRet = callLLVMIntrinOp.getResult(0);
3313
3314 mlir::Value result = mlir::LLVM::ExtractValueOp::create(
3315 rewriter, loc, intrinRet, ArrayRef<int64_t>{0})
3316 .getResult();
3317 mlir::Value overflow = mlir::LLVM::ExtractValueOp::create(
3318 rewriter, loc, intrinRet, ArrayRef<int64_t>{1})
3319 .getResult();
3320
3321 if (resultWidth < width) {
3322 mlir::Type resultLLVMTy = typeConverter->convertType(resultTy);
3323 auto truncResult =
3324 mlir::LLVM::TruncOp::create(rewriter, loc, resultLLVMTy, result);
3325
3326 // Extend the truncated result back to the encompassing type to check for
3327 // any overflows during the truncation.
3328 mlir::Value truncResultExt;
3329 if (resultSigned)
3330 truncResultExt = mlir::LLVM::SExtOp::create(
3331 rewriter, loc, encompassedLLVMTy, truncResult);
3332 else
3333 truncResultExt = mlir::LLVM::ZExtOp::create(
3334 rewriter, loc, encompassedLLVMTy, truncResult);
3335 auto truncOverflow = mlir::LLVM::ICmpOp::create(
3336 rewriter, loc, mlir::LLVM::ICmpPredicate::ne, truncResultExt, result);
3337
3338 result = truncResult;
3339 overflow = mlir::LLVM::OrOp::create(rewriter, loc, overflow, truncOverflow);
3340 }
3341
3342 mlir::Type boolLLVMTy =
3343 typeConverter->convertType(op.getOverflow().getType());
3344 if (boolLLVMTy != rewriter.getI1Type())
3345 overflow = mlir::LLVM::ZExtOp::create(rewriter, loc, boolLLVMTy, overflow);
3346
3347 rewriter.replaceOp(op, mlir::ValueRange{result, overflow});
3348
3349 return mlir::success();
3350}
3351
3352mlir::LogicalResult CIRToLLVMAddOverflowOpLowering::matchAndRewrite(
3353 cir::AddOverflowOp op, OpAdaptor adaptor,
3354 mlir::ConversionPatternRewriter &rewriter) const {
3355 return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "add");
3356}
3357
3358mlir::LogicalResult CIRToLLVMSubOverflowOpLowering::matchAndRewrite(
3359 cir::SubOverflowOp op, OpAdaptor adaptor,
3360 mlir::ConversionPatternRewriter &rewriter) const {
3361 return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "sub");
3362}
3363
3364mlir::LogicalResult CIRToLLVMMulOverflowOpLowering::matchAndRewrite(
3365 cir::MulOverflowOp op, OpAdaptor adaptor,
3366 mlir::ConversionPatternRewriter &rewriter) const {
3367 return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "mul");
3368}
3369
3370mlir::LogicalResult CIRToLLVMFrexpOpLowering::matchAndRewrite(
3371 cir::FrexpOp op, OpAdaptor adaptor,
3372 mlir::ConversionPatternRewriter &rewriter) const {
3373 mlir::Location loc = op.getLoc();
3374 mlir::Type fpLLVMTy =
3375 getTypeConverter()->convertType(op.getResult().getType());
3376 mlir::Type intLLVMTy = getTypeConverter()->convertType(op.getExp().getType());
3377
3378 auto structTy = mlir::LLVM::LLVMStructType::getLiteral(rewriter.getContext(),
3379 {fpLLVMTy, intLLVMTy});
3380
3381 auto callOp = createCallLLVMIntrinsicOp(rewriter, loc, "llvm.frexp", structTy,
3382 adaptor.getSrc());
3383 mlir::Value result = callOp.getResult(0);
3384
3385 mlir::Value mantissa =
3386 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 0);
3387 mlir::Value exponent =
3388 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 1);
3389 rewriter.replaceOp(op, mlir::ValueRange{mantissa, exponent});
3390 return mlir::success();
3391}
3392
3393mlir::LogicalResult CIRToLLVMModfOpLowering::matchAndRewrite(
3394 cir::ModfOp op, OpAdaptor adaptor,
3395 mlir::ConversionPatternRewriter &rewriter) const {
3396 mlir::Location loc = op.getLoc();
3397 mlir::Type fpLLVMTy =
3398 getTypeConverter()->convertType(op.getFractional().getType());
3399
3400 auto structTy = mlir::LLVM::LLVMStructType::getLiteral(rewriter.getContext(),
3401 {fpLLVMTy, fpLLVMTy});
3402
3403 auto callOp = createCallLLVMIntrinsicOp(rewriter, loc, "llvm.modf", structTy,
3404 adaptor.getSrc());
3405 mlir::Value result = callOp.getResult(0);
3406
3407 mlir::Value fractional =
3408 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 0);
3409 mlir::Value integral =
3410 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 1);
3411 rewriter.replaceOp(op, mlir::ValueRange{fractional, integral});
3412 return mlir::success();
3413}
3414
3415mlir::LogicalResult CIRToLLVMShiftOpLowering::matchAndRewrite(
3416 cir::ShiftOp op, OpAdaptor adaptor,
3417 mlir::ConversionPatternRewriter &rewriter) const {
3418 assert((op.getValue().getType() == op.getType()) &&
3419 "inconsistent operands' types NYI");
3420
3421 const mlir::Type llvmTy = getTypeConverter()->convertType(op.getType());
3422 mlir::Value amt = adaptor.getAmount();
3423 mlir::Value val = adaptor.getValue();
3424
3425 auto cirAmtTy = mlir::dyn_cast<cir::IntType>(op.getAmount().getType());
3426 bool isUnsigned;
3427 if (cirAmtTy) {
3428 auto cirValTy = mlir::cast<cir::IntType>(op.getValue().getType());
3429 isUnsigned = cirValTy.isUnsigned();
3430
3431 // Ensure shift amount is the same type as the value. Some undefined
3432 // behavior might occur in the casts below as per [C99 6.5.7.3].
3433 // Vector type shift amount needs no cast as type consistency is expected to
3434 // be already be enforced at CIRGen.
3435 if (cirAmtTy)
3436 amt = getLLVMIntCast(rewriter, amt, llvmTy, true, cirAmtTy.getWidth(),
3437 cirValTy.getWidth());
3438 } else {
3439 auto cirValVTy = mlir::cast<cir::VectorType>(op.getValue().getType());
3440 isUnsigned =
3441 mlir::cast<cir::IntType>(cirValVTy.getElementType()).isUnsigned();
3442 }
3443
3444 // Lower to the proper LLVM shift operation.
3445 if (op.getIsShiftleft()) {
3446 rewriter.replaceOpWithNewOp<mlir::LLVM::ShlOp>(op, llvmTy, val, amt);
3447 return mlir::success();
3448 }
3449
3450 if (isUnsigned)
3451 rewriter.replaceOpWithNewOp<mlir::LLVM::LShrOp>(op, llvmTy, val, amt);
3452 else
3453 rewriter.replaceOpWithNewOp<mlir::LLVM::AShrOp>(op, llvmTy, val, amt);
3454 return mlir::success();
3455}
3456
3457mlir::LogicalResult CIRToLLVMSelectOpLowering::matchAndRewrite(
3458 cir::SelectOp op, OpAdaptor adaptor,
3459 mlir::ConversionPatternRewriter &rewriter) const {
3460 auto getConstantBool = [](mlir::Value value) -> cir::BoolAttr {
3461 auto definingOp = value.getDefiningOp<cir::ConstantOp>();
3462 if (!definingOp)
3463 return {};
3464
3465 auto constValue = definingOp.getValueAttr<cir::BoolAttr>();
3466 if (!constValue)
3467 return {};
3468
3469 return constValue;
3470 };
3471
3472 // Two special cases in the LLVMIR codegen of select op:
3473 // - select %0, %1, false => and %0, %1
3474 // - select %0, true, %1 => or %0, %1
3475 if (mlir::isa<cir::BoolType>(op.getTrueValue().getType())) {
3476 cir::BoolAttr trueValue = getConstantBool(op.getTrueValue());
3477 cir::BoolAttr falseValue = getConstantBool(op.getFalseValue());
3478 if (falseValue && !falseValue.getValue()) {
3479 // select %0, %1, false => and %0, %1
3480 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(op, adaptor.getCondition(),
3481 adaptor.getTrueValue());
3482 return mlir::success();
3483 }
3484 if (trueValue && trueValue.getValue()) {
3485 // select %0, true, %1 => or %0, %1
3486 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(op, adaptor.getCondition(),
3487 adaptor.getFalseValue());
3488 return mlir::success();
3489 }
3490 }
3491
3492 mlir::Value llvmCondition = adaptor.getCondition();
3493 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(
3494 op, llvmCondition, adaptor.getTrueValue(), adaptor.getFalseValue());
3495
3496 return mlir::success();
3497}
3498
3499static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
3500 mlir::DataLayout &dataLayout) {
3501 converter.addConversion([&](cir::PointerType type) -> mlir::Type {
3502 mlir::ptr::MemorySpaceAttrInterface addrSpaceAttr = type.getAddrSpace();
3503 unsigned numericAS = 0;
3504
3505 if (auto targetAsAttr =
3506 mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
3507 addrSpaceAttr))
3508 numericAS = targetAsAttr.getValue();
3509 return mlir::LLVM::LLVMPointerType::get(type.getContext(), numericAS);
3510 });
3511 converter.addConversion([&](cir::VPtrType type) -> mlir::Type {
3513 return mlir::LLVM::LLVMPointerType::get(type.getContext());
3514 });
3515 converter.addConversion([&](cir::ArrayType type) -> mlir::Type {
3516 mlir::Type ty =
3517 convertTypeForMemory(converter, dataLayout, type.getElementType());
3518 // A null element type means an unsupported member (e.g. a _BitInt with
3519 // byte-array storage); propagate the conversion failure.
3520 if (!ty)
3521 return {};
3522 return mlir::LLVM::LLVMArrayType::get(ty, type.getSize());
3523 });
3524 converter.addConversion([&](cir::VectorType type) -> mlir::Type {
3525 // Vector-of-_BitInt memory layout is not modeled here (the element would
3526 // stay at its literal width); report a conversion failure rather than emit
3527 // an unvalidated vector.
3528 if (auto intTy = mlir::dyn_cast<cir::IntType>(type.getElementType());
3529 intTy && intTy.isBitInt())
3530 return {};
3531 const mlir::Type ty = converter.convertType(type.getElementType());
3532 return mlir::VectorType::get(type.getSize(), ty, {type.getIsScalable()});
3533 });
3534 converter.addConversion([&](cir::BoolType type) -> mlir::Type {
3535 return mlir::IntegerType::get(type.getContext(), 1,
3536 mlir::IntegerType::Signless);
3537 });
3538 converter.addConversion([&](cir::IntType type) -> mlir::Type {
3539 // LLVM doesn't work with signed types, so we drop the CIR signs here.
3540 return mlir::IntegerType::get(type.getContext(), type.getWidth());
3541 });
3542 converter.addConversion([&](cir::SingleType type) -> mlir::Type {
3543 return mlir::Float32Type::get(type.getContext());
3544 });
3545 converter.addConversion([&](cir::DoubleType type) -> mlir::Type {
3546 return mlir::Float64Type::get(type.getContext());
3547 });
3548 converter.addConversion([&](cir::FP80Type type) -> mlir::Type {
3549 return mlir::Float80Type::get(type.getContext());
3550 });
3551 converter.addConversion([&](cir::FP128Type type) -> mlir::Type {
3552 return mlir::Float128Type::get(type.getContext());
3553 });
3554 converter.addConversion([&](cir::LongDoubleType type) -> mlir::Type {
3555 return converter.convertType(type.getUnderlying());
3556 });
3557 converter.addConversion([&](cir::FP16Type type) -> mlir::Type {
3558 return mlir::Float16Type::get(type.getContext());
3559 });
3560 converter.addConversion([&](cir::BF16Type type) -> mlir::Type {
3561 return mlir::BFloat16Type::get(type.getContext());
3562 });
3563 converter.addConversion([&](cir::ComplexType type) -> mlir::Type {
3564 // A complex type is lowered to an LLVM struct that contains the real and
3565 // imaginary part as data fields.
3566 mlir::Type elementTy = converter.convertType(type.getElementType());
3567 mlir::Type structFields[2] = {elementTy, elementTy};
3568 return mlir::LLVM::LLVMStructType::getLiteral(type.getContext(),
3569 structFields);
3570 });
3571 converter.addConversion([&](cir::FuncType type) -> std::optional<mlir::Type> {
3572 auto result = converter.convertType(type.getReturnType());
3574 arguments.reserve(type.getNumInputs());
3575 if (converter.convertTypes(type.getInputs(), arguments).failed())
3576 return std::nullopt;
3577 auto varArg = type.isVarArg();
3578 return mlir::LLVM::LLVMFunctionType::get(result, arguments, varArg);
3579 });
3580 converter.addConversion([&](cir::StructType type) -> mlir::Type {
3582 for (mlir::Type ty : type.getMembers()) {
3583 mlir::Type memberTy = convertTypeForMemory(converter, dataLayout, ty);
3584 // A null member means an unsupported type (e.g. a _BitInt with byte-array
3585 // storage); propagate the conversion failure instead of building an
3586 // invalid struct body.
3587 if (!memberTy)
3588 return {};
3589 llvmMembers.push_back(memberTy);
3590 }
3591
3592 mlir::LLVM::LLVMStructType llvmStruct;
3593 if (type.getName()) {
3594 llvmStruct = mlir::LLVM::LLVMStructType::getIdentified(
3595 type.getContext(), type.getPrefixedName());
3596 if (llvmStruct.setBody(llvmMembers, type.getPacked()).failed())
3597 llvm_unreachable("Failed to set body of record");
3598 } else {
3599 llvmStruct = mlir::LLVM::LLVMStructType::getLiteral(
3600 type.getContext(), llvmMembers, type.getPacked());
3601 }
3602 return llvmStruct;
3603 });
3604 // Unions are lowered as only the largest member.
3605 converter.addConversion([&](cir::UnionType type) -> mlir::Type {
3607 if (!type.getMembers().empty())
3608 if (auto storage = type.getUnionStorageType(dataLayout)) {
3609 mlir::Type storageTy =
3610 convertTypeForMemory(converter, dataLayout, storage);
3611 if (!storageTy)
3612 return {};
3613 llvmMembers.push_back(storageTy);
3614 }
3615 if (mlir::Type pad = type.getPadding()) {
3616 mlir::Type padTy = convertTypeForMemory(converter, dataLayout, pad);
3617 if (!padTy)
3618 return {};
3619 llvmMembers.push_back(padTy);
3620 }
3621
3622 mlir::LLVM::LLVMStructType llvmStruct;
3623 if (type.getName()) {
3624 llvmStruct = mlir::LLVM::LLVMStructType::getIdentified(
3625 type.getContext(), type.getPrefixedName());
3626 if (llvmStruct.setBody(llvmMembers, type.getPacked()).failed())
3627 llvm_unreachable("Failed to set body of record");
3628 } else {
3629 llvmStruct = mlir::LLVM::LLVMStructType::getLiteral(
3630 type.getContext(), llvmMembers, type.getPacked());
3631 }
3632 return llvmStruct;
3633 });
3634 converter.addConversion([&](cir::VoidType type) -> mlir::Type {
3635 return mlir::LLVM::LLVMVoidType::get(type.getContext());
3636 });
3637}
3638
3640 mlir::ModuleOp module, StringRef globalXtorName, StringRef llvmXtorName,
3641 llvm::function_ref<std::pair<StringRef, int>(mlir::Attribute)> createXtor) {
3643 for (const mlir::NamedAttribute namedAttr : module->getAttrs()) {
3644 if (namedAttr.getName() == globalXtorName) {
3645 for (auto attr : mlir::cast<mlir::ArrayAttr>(namedAttr.getValue()))
3646 globalXtors.emplace_back(createXtor(attr));
3647 break;
3648 }
3649 }
3650
3651 if (globalXtors.empty())
3652 return;
3653
3654 mlir::OpBuilder builder(module.getContext());
3655 builder.setInsertionPointToEnd(&module.getBodyRegion().back());
3656
3657 // Create a global array llvm.global_ctors with element type of
3658 // struct { i32, ptr, ptr }
3659 auto ctorPFTy = mlir::LLVM::LLVMPointerType::get(builder.getContext());
3660 llvm::SmallVector<mlir::Type> ctorStructFields;
3661 ctorStructFields.push_back(builder.getI32Type());
3662 ctorStructFields.push_back(ctorPFTy);
3663 ctorStructFields.push_back(ctorPFTy);
3664
3665 auto ctorStructTy = mlir::LLVM::LLVMStructType::getLiteral(
3666 builder.getContext(), ctorStructFields);
3667 auto ctorStructArrayTy =
3668 mlir::LLVM::LLVMArrayType::get(ctorStructTy, globalXtors.size());
3669
3670 mlir::Location loc = module.getLoc();
3671 auto newGlobalOp = mlir::LLVM::GlobalOp::create(
3672 builder, loc, ctorStructArrayTy, /*constant=*/false,
3673 mlir::LLVM::Linkage::Appending, llvmXtorName, mlir::Attribute());
3674
3675 builder.createBlock(&newGlobalOp.getRegion());
3676 builder.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());
3677
3678 mlir::Value result =
3679 mlir::LLVM::UndefOp::create(builder, loc, ctorStructArrayTy);
3680
3681 for (auto [index, fn] : llvm::enumerate(globalXtors)) {
3682 mlir::Value structInit =
3683 mlir::LLVM::UndefOp::create(builder, loc, ctorStructTy);
3684 mlir::Value initPriority = mlir::LLVM::ConstantOp::create(
3685 builder, loc, ctorStructFields[0], fn.second);
3686 mlir::Value initFuncAddr = mlir::LLVM::AddressOfOp::create(
3687 builder, loc, ctorStructFields[1], fn.first);
3688 mlir::Value initAssociate =
3689 mlir::LLVM::ZeroOp::create(builder, loc, ctorStructFields[2]);
3690 // Literal zero makes the InsertValueOp::create ambiguous.
3692 structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
3693 initPriority, zero);
3694 structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
3695 initFuncAddr, 1);
3696 // TODO: handle associated data for initializers.
3697 structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
3698 initAssociate, 2);
3699 result = mlir::LLVM::InsertValueOp::create(builder, loc, result, structInit,
3700 index);
3701 }
3702
3703 mlir::LLVM::ReturnOp::create(builder, loc, result);
3704}
3705
3706mlir::LogicalResult CIRToLLVMObjSizeOpLowering::matchAndRewrite(
3707 cir::ObjSizeOp op, OpAdaptor adaptor,
3708 mlir::ConversionPatternRewriter &rewriter) const {
3709 mlir::Type llvmResTy = getTypeConverter()->convertType(op.getType());
3710 mlir::Location loc = op->getLoc();
3711
3712 mlir::IntegerType i1Ty = rewriter.getI1Type();
3713
3714 auto i1Val = [&rewriter, &loc, &i1Ty](bool val) {
3715 return mlir::LLVM::ConstantOp::create(rewriter, loc, i1Ty, val);
3716 };
3717
3718 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.objectsize", llvmResTy,
3719 {
3720 adaptor.getPtr(),
3721 i1Val(op.getMin()),
3722 i1Val(op.getNullunknown()),
3723 i1Val(op.getDynamic()),
3724 });
3725
3726 return mlir::LogicalResult::success();
3727}
3728
3729//===----------------------------------------------------------------------===//
3730// @llvm.global.annotations emission
3731//===----------------------------------------------------------------------===//
3732
3733namespace {
3734constexpr StringRef llvmMetadataSectionName = "llvm.metadata";
3735
3736/// Get-or-create a private constant string global in the llvm.metadata
3737/// section, deduplicated by string content.
3738mlir::LLVM::GlobalOp
3739getOrCreateAnnotationStringGlobal(mlir::OpBuilder &builder, mlir::Location loc,
3740 mlir::ModuleOp module, llvm::StringRef str,
3741 llvm::StringMap<mlir::LLVM::GlobalOp> &cache,
3742 bool isArg) {
3743 auto it = cache.find(str);
3744 if (it != cache.end())
3745 return it->second;
3746
3747 auto i8Ty = mlir::IntegerType::get(module.getContext(), 8);
3748 auto arrayTy = mlir::LLVM::LLVMArrayType::get(i8Ty, str.size() + 1);
3749 std::string name = ".str";
3750 if (!cache.empty())
3751 name += "." + std::to_string(cache.size());
3752 name += ".annotation";
3753 if (isArg)
3754 name += ".arg";
3755
3756 mlir::LLVM::GlobalOp strGlobal = mlir::LLVM::GlobalOp::create(
3757 builder, loc, arrayTy, /*isConstant=*/true, mlir::LLVM::Linkage::Private,
3758 name, mlir::StringAttr::get(module.getContext(), std::string(str) + '\0'),
3759 /*alignment=*/isArg ? 1 : 0);
3760 if (!isArg)
3761 strGlobal.setSection(llvmMetadataSectionName);
3762 strGlobal.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Global);
3763 strGlobal.setDsoLocal(true);
3764 cache[str] = strGlobal;
3765 return strGlobal;
3766}
3767
3768/// Get-or-create a private constant struct holding the annotation arguments,
3769/// deduplicated by ArrayAttr identity.
3770mlir::LLVM::GlobalOp getOrCreateAnnotationArgsVar(
3771 mlir::OpBuilder &builder, mlir::Location loc, mlir::ModuleOp module,
3772 mlir::ArrayAttr argsAttr,
3773 llvm::StringMap<mlir::LLVM::GlobalOp> &argStringCache,
3774 llvm::MapVector<mlir::ArrayAttr, mlir::LLVM::GlobalOp> &argsCache) {
3775 auto it = argsCache.find(argsAttr);
3776 if (it != argsCache.end())
3777 return it->second;
3778
3779 auto ptrTy = mlir::LLVM::LLVMPointerType::get(builder.getContext());
3780
3781 llvm::SmallVector<mlir::Type> fieldTypes;
3782 for (mlir::Attribute arg : argsAttr) {
3783 if (mlir::isa<mlir::StringAttr>(arg))
3784 fieldTypes.push_back(ptrTy);
3785 else if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(arg))
3786 fieldTypes.push_back(intAttr.getType());
3787 else
3788 llvm_unreachable("Unsupported annotation arg type");
3789 }
3790
3791 auto structTy =
3792 mlir::LLVM::LLVMStructType::getLiteral(builder.getContext(), fieldTypes);
3793 std::string name = ".args";
3794 if (!argsCache.empty())
3795 name += "." + std::to_string(argsCache.size());
3796 name += ".annotation";
3797
3798 mlir::LLVM::GlobalOp argsGlobal = mlir::LLVM::GlobalOp::create(
3799 builder, loc, structTy, /*isConstant=*/true, mlir::LLVM::Linkage::Private,
3800 name, mlir::Attribute());
3801 argsGlobal.setSection(llvmMetadataSectionName);
3802 argsGlobal.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Global);
3803 argsGlobal.setDsoLocal(true);
3804
3805 // Build the initializer block.
3806 argsGlobal.getRegion().push_back(new mlir::Block());
3807 mlir::OpBuilder initBuilder(module.getContext());
3808 initBuilder.setInsertionPointToEnd(argsGlobal.getInitializerBlock());
3809
3810 mlir::Value structInit =
3811 mlir::LLVM::UndefOp::create(initBuilder, loc, structTy);
3812 for (auto [idx, arg] : llvm::enumerate(argsAttr)) {
3813 if (auto strArg = mlir::dyn_cast<mlir::StringAttr>(arg)) {
3814 mlir::LLVM::GlobalOp strGlobal = getOrCreateAnnotationStringGlobal(
3815 builder, loc, module, strArg.getValue(), argStringCache,
3816 /*isArg=*/true);
3817 mlir::LLVM::AddressOfOp strAddr = mlir::LLVM::AddressOfOp::create(
3818 initBuilder, loc, ptrTy, strGlobal.getSymName());
3819 structInit = mlir::LLVM::InsertValueOp::create(initBuilder, loc,
3820 structInit, strAddr, idx);
3821 } else if (auto intArg = mlir::dyn_cast<mlir::IntegerAttr>(arg)) {
3822 mlir::LLVM::ConstantOp intConst = mlir::LLVM::ConstantOp::create(
3823 initBuilder, loc, intArg.getType(), intArg.getValue());
3824 structInit = mlir::LLVM::InsertValueOp::create(initBuilder, loc,
3825 structInit, intConst, idx);
3826 } else {
3827 llvm_unreachable("Unsupported annotation arg type");
3828 }
3829 }
3830 mlir::LLVM::ReturnOp::create(initBuilder, loc, structInit);
3831
3832 argsCache[argsAttr] = argsGlobal;
3833 return argsGlobal;
3834}
3835
3836/// Resolve a possibly-fused MLIR Location to a FileLineColLoc, returning
3837/// {filename, line}. Returns {empty, 0} if no usable file location is found.
3838std::pair<llvm::StringRef, unsigned> extractFileLine(mlir::Location loc) {
3839 mlir::Location resolved = loc;
3840 if (auto fused = mlir::dyn_cast<mlir::FusedLoc>(resolved)) {
3841 if (!fused.getLocations().empty())
3842 resolved = fused.getLocations()[0];
3843 }
3844 if (auto fl = mlir::dyn_cast<mlir::FileLineColLoc>(resolved))
3845 return {fl.getFilename().getValue(), fl.getLine()};
3846 return {"", 0};
3847}
3848} // namespace
3849
3851 auto handleArray = [&](mlir::StringAttr symName, mlir::ArrayAttr arr,
3852 mlir::Location loc) {
3853 if (!arr)
3854 return;
3855 for (mlir::Attribute a : arr)
3856 if (auto annot = mlir::dyn_cast<cir::AnnotationAttr>(a))
3857 collectedAnnotations.emplace_back(symName, annot, loc);
3858 };
3859
3860 // Walk in IR order: GlobalOps first (they appear before functions in the
3861 // module body), then FuncOps. This matches OGCG's emission order.
3862 module.walk([&](cir::GlobalOp op) {
3863 handleArray(op.getSymNameAttr(), op.getAnnotationsAttr(), op.getLoc());
3864 });
3865 module.walk([&](cir::FuncOp op) {
3866 handleArray(op.getSymNameAttr(), op.getAnnotationsAttr(), op.getLoc());
3867 });
3868}
3869
3871 if (collectedAnnotations.empty())
3872 return;
3873
3874 mlir::MLIRContext *ctx = module.getContext();
3875 mlir::OpBuilder builder(ctx);
3876 builder.setInsertionPointToEnd(&module.getBodyRegion().back());
3877
3878 auto ptrTy = mlir::LLVM::LLVMPointerType::get(ctx);
3879 auto i32Ty = builder.getI32Type();
3880
3881 // Each entry: { ptr, ptr, ptr, i32, ptr }.
3882 auto entryTy = mlir::LLVM::LLVMStructType::getLiteral(
3883 ctx, {ptrTy, ptrTy, ptrTy, i32Ty, ptrTy});
3884 auto arrayTy =
3885 mlir::LLVM::LLVMArrayType::get(entryTy, collectedAnnotations.size());
3886
3887 mlir::Location moduleLoc = module.getLoc();
3888 auto annotationsGlobal = mlir::LLVM::GlobalOp::create(
3889 builder, moduleLoc, arrayTy, /*isConstant=*/false,
3890 mlir::LLVM::Linkage::Appending, "llvm.global.annotations",
3891 mlir::Attribute());
3892 annotationsGlobal.setSection(llvmMetadataSectionName);
3893
3894 // Strings/args constants must come *before* @llvm.global.annotations to
3895 // match OGCG output order. Insert them just before the annotations global.
3896 mlir::OpBuilder constsBuilder(ctx);
3897 constsBuilder.setInsertionPoint(annotationsGlobal);
3898
3899 llvm::StringMap<mlir::LLVM::GlobalOp> stringCache;
3900 llvm::StringMap<mlir::LLVM::GlobalOp> argStringCache;
3901 llvm::MapVector<mlir::ArrayAttr, mlir::LLVM::GlobalOp> argsCache;
3902
3903 // Build the initializer block of @llvm.global.annotations.
3904 annotationsGlobal.getRegion().push_back(new mlir::Block());
3905 mlir::OpBuilder initBuilder(ctx);
3906 initBuilder.setInsertionPointToEnd(annotationsGlobal.getInitializerBlock());
3907
3908 mlir::Value arrayVal =
3909 mlir::LLVM::UndefOp::create(initBuilder, moduleLoc, arrayTy);
3910
3911 for (auto [idx, entry] : llvm::enumerate(collectedAnnotations)) {
3912 mlir::Value entryVal =
3913 mlir::LLVM::UndefOp::create(initBuilder, moduleLoc, entryTy);
3914
3915 // Field 0: ptr to the annotated symbol. (Literal zero is ambiguous on
3916 // InsertValueOp::create, wrap in a SmallVector.)
3918 mlir::LLVM::AddressOfOp symAddr = mlir::LLVM::AddressOfOp::create(
3919 initBuilder, moduleLoc, ptrTy, entry.symName.getValue());
3920 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3921 entryVal, symAddr, zero);
3922
3923 // Field 1: ptr to the annotation name string.
3924 mlir::LLVM::GlobalOp nameGlobal = getOrCreateAnnotationStringGlobal(
3925 constsBuilder, moduleLoc, module, entry.annotation.getName().getValue(),
3926 stringCache, /*isArg=*/false);
3927 mlir::LLVM::AddressOfOp nameAddr = mlir::LLVM::AddressOfOp::create(
3928 initBuilder, moduleLoc, ptrTy, nameGlobal.getSymName());
3929 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3930 entryVal, nameAddr, 1);
3931
3932 // Fields 2 and 3: ptr to filename string and line number.
3933 auto [filename, line] = extractFileLine(entry.loc);
3934 mlir::LLVM::GlobalOp fileGlobal = getOrCreateAnnotationStringGlobal(
3935 constsBuilder, moduleLoc, module, filename, stringCache,
3936 /*isArg=*/false);
3937 mlir::LLVM::AddressOfOp fileAddr = mlir::LLVM::AddressOfOp::create(
3938 initBuilder, moduleLoc, ptrTy, fileGlobal.getSymName());
3939 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3940 entryVal, fileAddr, 2);
3941 mlir::LLVM::ConstantOp lineConst =
3942 mlir::LLVM::ConstantOp::create(initBuilder, moduleLoc, i32Ty, line);
3943 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3944 entryVal, lineConst, 3);
3945
3946 // Field 4: ptr to args, or null if none.
3947 mlir::ArrayAttr args = entry.annotation.getArgs();
3948 mlir::Value argsField;
3949 if (!args || args.empty()) {
3950 argsField = mlir::LLVM::ZeroOp::create(initBuilder, moduleLoc, ptrTy);
3951 } else {
3952 mlir::LLVM::GlobalOp argsGlobal = getOrCreateAnnotationArgsVar(
3953 constsBuilder, moduleLoc, module, args, argStringCache, argsCache);
3954 argsField = mlir::LLVM::AddressOfOp::create(initBuilder, moduleLoc, ptrTy,
3955 argsGlobal.getSymName());
3956 }
3957 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3958 entryVal, argsField, 4);
3959
3960 arrayVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3961 arrayVal, entryVal, idx);
3962 }
3963
3964 mlir::LLVM::ReturnOp::create(initBuilder, moduleLoc, arrayVal);
3965}
3966
3968 LLVMBlockAddressInfo &blockInfoAddr) {
3969
3970 mlir::ModuleOp module = getOperation();
3971 mlir::OpBuilder opBuilder(module.getContext());
3972 for (auto &[blockAddOp, blockInfo] :
3973 blockInfoAddr.getUnresolvedBlockAddress()) {
3974 mlir::LLVM::BlockTagOp resolvedLabel =
3975 blockInfoAddr.lookupBlockTag(blockInfo);
3976 assert(resolvedLabel && "expected BlockTagOp to already be emitted");
3977 mlir::FlatSymbolRefAttr fnSym = blockInfo.getFunc();
3978 auto blkAddTag = mlir::LLVM::BlockAddressAttr::get(
3979 opBuilder.getContext(), fnSym, resolvedLabel.getTagAttr());
3980 blockAddOp.setBlockAddrAttr(blkAddTag);
3981 }
3982 blockInfoAddr.clearUnresolvedMap();
3983}
3984
3985void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) {
3986 // Lower the module attributes to LLVM equivalents.
3987 if (mlir::Attribute tripleAttr =
3988 module->getAttr(cir::CIRDialect::getTripleAttrName()))
3989 module->setAttr(mlir::LLVM::LLVMDialect::getTargetTripleAttrName(),
3990 tripleAttr);
3991
3992 if (mlir::Attribute asmAttr =
3993 module->getAttr(cir::CIRDialect::getModuleLevelAsmAttrName()))
3994 module->setAttr(mlir::LLVM::LLVMDialect::getModuleLevelAsmAttrName(),
3995 asmAttr);
3996}
3997
3999 llvm::TimeTraceScope scope("Convert CIR to LLVM Pass");
4000
4001 mlir::ModuleOp module = getOperation();
4002 mlir::DataLayout dl(module);
4003 mlir::LLVMTypeConverter converter(&getContext());
4004 prepareTypeConverter(converter, dl);
4005
4006 /// Tracks the state required to lower CIR `LabelOp` and `BlockAddressOp`.
4007 /// Maps labels to their corresponding `BlockTagOp` and keeps bookkeeping
4008 /// of unresolved `BlockAddressOp`s until they are matched with the
4009 /// corresponding `BlockTagOp` in `resolveBlockAddressOp`.
4010 LLVMBlockAddressInfo blockInfoAddr;
4011 /// Cached symbol table collection used by call lowering patterns to avoid
4012 /// repeated O(M) module-wide symbol scans for every call site.
4013 mlir::SymbolTableCollection symbolTables;
4014 mlir::RewritePatternSet patterns(&getContext());
4015 patterns.add<CIRToLLVMBlockAddressOpLowering, CIRToLLVMGlobalOpLowering,
4016 CIRToLLVMLabelOpLowering>(converter, patterns.getContext(), dl,
4017 blockInfoAddr);
4018 patterns.add<CIRToLLVMCallOpLowering, CIRToLLVMTryCallOpLowering>(
4019 converter, patterns.getContext(), dl, symbolTables);
4020
4021 patterns.add<
4022#define GET_LLVM_LOWERING_PATTERNS_LIST
4023#include "clang/CIR/Dialect/IR/CIRLowering.inc"
4024#undef GET_LLVM_LOWERING_PATTERNS_LIST
4025 >(converter, patterns.getContext(), dl);
4026
4027 processCIRAttrs(module);
4028
4029 // Collect annotation info from cir.func / cir.global before conversion;
4030 // the annotations attribute is filtered out during FuncOp/GlobalOp lowering.
4032
4033 mlir::ConversionTarget target(getContext());
4034 target.addLegalOp<mlir::ModuleOp>();
4035 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
4036 mlir::configureOpenMPToLLVMConversionLegality(target, converter);
4037 target.addLegalDialect<mlir::omp::OpenMPDialect>();
4038 mlir::populateOpenMPToLLVMConversionPatterns(converter, patterns);
4039 target.addIllegalDialect<mlir::BuiltinDialect, cir::CIRDialect,
4040 mlir::func::FuncDialect>();
4041
4043 ops.push_back(module);
4044 cir::collectUnreachable(module, ops);
4045
4046 if (failed(applyPartialConversion(ops, target, std::move(patterns))))
4047 signalPassFailure();
4048
4049 // Emit the llvm.global_ctors array.
4050 buildCtorDtorList(module, cir::CIRDialect::getGlobalCtorsAttrName(),
4051 "llvm.global_ctors", [](mlir::Attribute attr) {
4052 auto ctorAttr = mlir::cast<cir::GlobalCtorAttr>(attr);
4053 return std::make_pair(ctorAttr.getName(),
4054 ctorAttr.getPriority());
4055 });
4056 // Emit the llvm.global_dtors array.
4057 buildCtorDtorList(module, cir::CIRDialect::getGlobalDtorsAttrName(),
4058 "llvm.global_dtors", [](mlir::Attribute attr) {
4059 auto dtorAttr = mlir::cast<cir::GlobalDtorAttr>(attr);
4060 return std::make_pair(dtorAttr.getName(),
4061 dtorAttr.getPriority());
4062 });
4063 // Emit @llvm.global.annotations from the previously-collected entries.
4065
4066 resolveBlockAddressOp(blockInfoAddr);
4067}
4068
4069mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(
4070 cir::BrOp op, OpAdaptor adaptor,
4071 mlir::ConversionPatternRewriter &rewriter) const {
4072 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(op, adaptor.getOperands(),
4073 op.getDest());
4074 return mlir::LogicalResult::success();
4075}
4076
4077mlir::LogicalResult CIRToLLVMGetMemberOpLowering::matchAndRewrite(
4078 cir::GetMemberOp op, OpAdaptor adaptor,
4079 mlir::ConversionPatternRewriter &rewriter) const {
4080 mlir::Type llResTy = getTypeConverter()->convertType(op.getType());
4081 mlir::Type pointee = op.getAddrTy().getPointee();
4082
4083 if (mlir::isa<cir::UnionType>(pointee)) {
4084 // Union members share the address space, so we just need a bitcast to
4085 // conform to type-checking.
4086 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(op, llResTy,
4087 adaptor.getAddr());
4088 return mlir::success();
4089 }
4090
4091 auto structTy = mlir::cast<cir::StructType>(pointee);
4092 // Since the base address is a pointer to an aggregate, the first offset
4093 // is always zero. The second offset tells us which member it will access.
4094 llvm::SmallVector<mlir::LLVM::GEPArg, 2> offset{0, op.getIndex()};
4095 const mlir::Type elementTy = getTypeConverter()->convertType(structTy);
4096 // Struct member accesses are always inbounds and nuw: the base pointer
4097 // is valid and the member offset is a positive, constant offset within
4098 // the struct layout, so it cannot wrap. This matches LLVM's
4099 // IRBuilder::CreateStructGEP.
4100 mlir::LLVM::GEPNoWrapFlags flags =
4101 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
4102 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4103 op, llResTy, elementTy, adaptor.getAddr(), offset, flags);
4104 return mlir::success();
4105}
4106
4107mlir::LogicalResult CIRToLLVMExtractMemberOpLowering::matchAndRewrite(
4108 cir::ExtractMemberOp op, OpAdaptor adaptor,
4109 mlir::ConversionPatternRewriter &rewriter) const {
4110 std::int64_t indices[1] = {static_cast<std::int64_t>(op.getIndex())};
4111
4112 if (mlir::isa<cir::UnionType>(op.getRecord().getType())) {
4113 op.emitError("cir.extract_member cannot extract member from a union");
4114 return mlir::failure();
4115 }
4116
4117 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
4118 op, adaptor.getRecord(), indices);
4119 return mlir::success();
4120}
4121
4122mlir::LogicalResult CIRToLLVMInsertMemberOpLowering::matchAndRewrite(
4123 cir::InsertMemberOp op, OpAdaptor adaptor,
4124 mlir::ConversionPatternRewriter &rewriter) const {
4125 std::int64_t indecies[1] = {static_cast<std::int64_t>(op.getIndex())};
4126
4127 if (mlir::isa<cir::UnionType>(op.getRecord().getType())) {
4128 op.emitError("cir.update_member cannot update member of a union");
4129 return mlir::failure();
4130 }
4131
4132 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
4133 op, adaptor.getRecord(), adaptor.getValue(), indecies);
4134 return mlir::success();
4135}
4136
4137void createLLVMFuncOpIfNotExist(mlir::ConversionPatternRewriter &rewriter,
4138 mlir::Operation *srcOp, llvm::StringRef fnName,
4139 mlir::Type fnTy,
4140 mlir::ArrayAttr argAttrs = nullptr,
4141 mlir::ArrayAttr resAttrs = nullptr) {
4142 mlir::ModuleOp modOp = srcOp->getParentOfType<mlir::ModuleOp>();
4143 mlir::Operation *sourceSymbol =
4144 mlir::SymbolTable::lookupSymbolIn(modOp, fnName);
4145 if (!sourceSymbol) {
4146 mlir::OpBuilder::InsertionGuard guard(rewriter);
4147 auto enclosingFnOp = srcOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
4148 rewriter.setInsertionPoint(enclosingFnOp);
4149 auto fn =
4150 mlir::LLVM::LLVMFuncOp::create(rewriter, srcOp->getLoc(), fnName, fnTy);
4151 if (argAttrs)
4152 fn.setArgAttrsAttr(argAttrs);
4153 if (resAttrs)
4154 fn.setResAttrsAttr(resAttrs);
4155 }
4156}
4157
4158mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite(
4159 cir::ThrowOp op, OpAdaptor adaptor,
4160 mlir::ConversionPatternRewriter &rewriter) const {
4161 mlir::Location loc = op.getLoc();
4162 auto voidTy = mlir::LLVM::LLVMVoidType::get(getContext());
4163
4164 if (op.rethrows()) {
4165 auto funcTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {});
4166
4167 // Get or create `declare void @__cxa_rethrow()`
4168 const llvm::StringRef functionName = "__cxa_rethrow";
4169 createLLVMFuncOpIfNotExist(rewriter, op, functionName, funcTy);
4170
4171 auto cxaRethrow = mlir::LLVM::CallOp::create(
4172 rewriter, loc, mlir::TypeRange{}, functionName);
4173
4174 rewriter.replaceOp(op, cxaRethrow);
4175 return mlir::success();
4176 }
4177
4178 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4179 auto fnTy = mlir::LLVM::LLVMFunctionType::get(
4180 voidTy, {llvmPtrTy, llvmPtrTy, llvmPtrTy});
4181
4182 // Get or create `declare void @__cxa_throw(ptr, ptr, ptr)`
4183 const llvm::StringRef fnName = "__cxa_throw";
4184 createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
4185
4186 mlir::Value typeInfo = mlir::LLVM::AddressOfOp::create(
4187 rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
4188 adaptor.getTypeInfoAttr());
4189
4190 mlir::Value dtor;
4191 if (op.getDtor()) {
4192 dtor = mlir::LLVM::AddressOfOp::create(rewriter, loc, llvmPtrTy,
4193 adaptor.getDtorAttr());
4194 } else {
4195 dtor = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
4196 }
4197
4198 auto cxaThrowCall = mlir::LLVM::CallOp::create(
4199 rewriter, loc, mlir::TypeRange{}, fnName,
4200 mlir::ValueRange{adaptor.getExceptionPtr(), typeInfo, dtor});
4201
4202 rewriter.replaceOp(op, cxaThrowCall);
4203 return mlir::success();
4204}
4205
4206mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
4207 cir::AllocExceptionOp op, OpAdaptor adaptor,
4208 mlir::ConversionPatternRewriter &rewriter) const {
4209 // Get or create `declare ptr @__cxa_allocate_exception(i64)`
4210 StringRef fnName = "__cxa_allocate_exception";
4211 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4212 auto int64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
4213 auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {int64Ty});
4214
4215 createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
4216 auto exceptionSize = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
4217 adaptor.getSizeAttr());
4218
4219 auto allocaExceptionCall = mlir::LLVM::CallOp::create(
4220 rewriter, op.getLoc(), mlir::TypeRange{llvmPtrTy}, fnName,
4221 mlir::ValueRange{exceptionSize});
4222
4223 rewriter.replaceOp(op, allocaExceptionCall);
4224 return mlir::success();
4225}
4226
4227static mlir::LLVM::LLVMStructType
4228getLLVMLandingPadStructTy(mlir::ConversionPatternRewriter &rewriter) {
4229 // Create the landing pad type: struct { ptr, i32 }
4230 mlir::MLIRContext *ctx = rewriter.getContext();
4231 auto llvmPtr = mlir::LLVM::LLVMPointerType::get(ctx);
4232 llvm::SmallVector<mlir::Type> structFields = {llvmPtr, rewriter.getI32Type()};
4233 return mlir::LLVM::LLVMStructType::getLiteral(ctx, structFields);
4234}
4235
4236mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite(
4237 cir::EhInflightOp op, OpAdaptor adaptor,
4238 mlir::ConversionPatternRewriter &rewriter) const {
4239 auto llvmFn = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
4240 assert(llvmFn && "expected LLVM function parent");
4241 mlir::Block *entryBlock = &llvmFn.getRegion().front();
4242 assert(entryBlock->isEntryBlock());
4243
4244 mlir::ArrayAttr catchListAttr = op.getCatchTypeListAttr();
4245 mlir::SmallVector<mlir::Value> catchSymAddrs;
4246
4247 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4248 mlir::Location loc = op.getLoc();
4249
4250 // %landingpad = landingpad { ptr, i32 }
4251 // Note that since llvm.landingpad has to be the first operation on the
4252 // block, any needed value for its operands has to be added somewhere else.
4253 if (catchListAttr) {
4254 // catch ptr @_ZTIi
4255 // catch ptr @_ZTIPKc
4256 for (mlir::Attribute catchAttr : catchListAttr) {
4257 auto symAttr = cast<mlir::FlatSymbolRefAttr>(catchAttr);
4258 // Generate `llvm.mlir.addressof` for each symbol, and place those
4259 // operations in the LLVM function entry basic block.
4260 mlir::OpBuilder::InsertionGuard guard(rewriter);
4261 rewriter.setInsertionPointToStart(entryBlock);
4262 mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
4263 rewriter, loc, llvmPtrTy, symAttr.getValue());
4264 catchSymAddrs.push_back(addrOp);
4265 }
4266 }
4267
4268 // Emit a catch-all clause (catch ptr null) when:
4269 // - The catch_all attribute is set (typed catches + catch-all), or
4270 // - No typed catches and no cleanup (legacy pure catch-all form)
4271 if (op.getCatchAll() || (!catchListAttr && !op.getCleanup())) {
4272 mlir::OpBuilder::InsertionGuard guard(rewriter);
4273 rewriter.setInsertionPointToStart(entryBlock);
4274 mlir::Value nullOp = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
4275 catchSymAddrs.push_back(nullOp);
4276 }
4277
4278 // %slot = extractvalue { ptr, i32 } %x, 0
4279 // %selector = extractvalue { ptr, i32 } %x, 1
4280 mlir::LLVM::LLVMStructType llvmLandingPadStructTy =
4281 getLLVMLandingPadStructTy(rewriter);
4282 auto landingPadOp = mlir::LLVM::LandingpadOp::create(
4283 rewriter, loc, llvmLandingPadStructTy, catchSymAddrs);
4284
4285 // The LLVM cleanup flag is only needed when there is no catch-all handler,
4286 // since catch-all (catch ptr null) already ensures the personality function
4287 // enters the landing pad for all exception types.
4288 if (op.getCleanup() && !op.getCatchAll())
4289 landingPadOp.setCleanup(true);
4290
4291 mlir::Value slot =
4292 mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 0);
4293 mlir::Value selector =
4294 mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 1);
4295 rewriter.replaceOp(op, mlir::ValueRange{slot, selector});
4296
4297 return mlir::success();
4298}
4299
4300mlir::LogicalResult CIRToLLVMResumeFlatOpLowering::matchAndRewrite(
4301 cir::ResumeFlatOp op, OpAdaptor adaptor,
4302 mlir::ConversionPatternRewriter &rewriter) const {
4303 // %lpad.val = insertvalue { ptr, i32 } poison, ptr %exception_ptr, 0
4304 // %lpad.val2 = insertvalue { ptr, i32 } %lpad.val, i32 %selector, 1
4305 // resume { ptr, i32 } %lpad.val2
4306 mlir::Type llvmLandingPadStructTy = getLLVMLandingPadStructTy(rewriter);
4307 mlir::Value poison = mlir::LLVM::PoisonOp::create(rewriter, op.getLoc(),
4308 llvmLandingPadStructTy);
4309
4310 SmallVector<int64_t> slotIdx = {0};
4311 mlir::Value slot = mlir::LLVM::InsertValueOp::create(
4312 rewriter, op.getLoc(), poison, adaptor.getExceptionPtr(), slotIdx);
4313
4314 SmallVector<int64_t> selectorIdx = {1};
4315 mlir::Value selector = mlir::LLVM::InsertValueOp::create(
4316 rewriter, op.getLoc(), slot, adaptor.getTypeId(), selectorIdx);
4317
4318 rewriter.replaceOpWithNewOp<mlir::LLVM::ResumeOp>(op, selector);
4319 return mlir::success();
4320}
4321
4322mlir::LogicalResult CIRToLLVMEhTypeIdOpLowering::matchAndRewrite(
4323 cir::EhTypeIdOp op, OpAdaptor adaptor,
4324 mlir::ConversionPatternRewriter &rewriter) const {
4325 mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
4326 rewriter, op.getLoc(),
4327 mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
4328 op.getTypeSymAttr());
4329 rewriter.replaceOpWithNewOp<mlir::LLVM::EhTypeidForOp>(
4330 op, rewriter.getI32Type(), addrOp);
4331 return mlir::success();
4332}
4333
4334mlir::LogicalResult CIRToLLVMEhSetjmpOpLowering::matchAndRewrite(
4335 cir::EhSetjmpOp op, OpAdaptor adaptor,
4336 mlir::ConversionPatternRewriter &rewriter) const {
4337 mlir::Type returnType = typeConverter->convertType(op.getType());
4338 mlir::LLVM::CallIntrinsicOp newOp =
4339 createCallLLVMIntrinsicOp(rewriter, op.getLoc(), "llvm.eh.sjlj.setjmp",
4340 returnType, adaptor.getEnv());
4341 rewriter.replaceOp(op, newOp);
4342 return mlir::success();
4343}
4344
4345mlir::LogicalResult CIRToLLVMEhLongjmpOpLowering::matchAndRewrite(
4346 cir::EhLongjmpOp op, OpAdaptor adaptor,
4347 mlir::ConversionPatternRewriter &rewriter) const {
4348 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.eh.sjlj.longjmp",
4349 /*resultTy=*/{}, adaptor.getOperands());
4350 return mlir::success();
4351}
4352
4353mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite(
4354 cir::TrapOp op, OpAdaptor adaptor,
4355 mlir::ConversionPatternRewriter &rewriter) const {
4356 mlir::Location loc = op->getLoc();
4357 rewriter.eraseOp(op);
4358
4359 mlir::LLVM::Trap::create(rewriter, loc);
4360
4361 // Note that the call to llvm.trap is not a terminator in LLVM dialect.
4362 // So we must emit an additional llvm.unreachable to terminate the current
4363 // block.
4364 mlir::LLVM::UnreachableOp::create(rewriter, loc);
4365
4366 return mlir::success();
4367}
4368
4369static mlir::Value
4370getValueForVTableSymbol(mlir::Operation *op,
4371 mlir::ConversionPatternRewriter &rewriter,
4372 const mlir::TypeConverter *converter,
4373 mlir::FlatSymbolRefAttr nameAttr, mlir::Type &eltType) {
4374 auto module = op->getParentOfType<mlir::ModuleOp>();
4375 mlir::Operation *symbol = mlir::SymbolTable::lookupSymbolIn(module, nameAttr);
4376 if (auto llvmSymbol = mlir::dyn_cast<mlir::LLVM::GlobalOp>(symbol)) {
4377 eltType = llvmSymbol.getType();
4378 } else if (auto cirSymbol = mlir::dyn_cast<cir::GlobalOp>(symbol)) {
4379 eltType = converter->convertType(cirSymbol.getSymType());
4380 } else {
4381 op->emitError() << "unexpected symbol type for " << symbol;
4382 return {};
4383 }
4384
4385 return mlir::LLVM::AddressOfOp::create(
4386 rewriter, op->getLoc(),
4387 mlir::LLVM::LLVMPointerType::get(op->getContext()), nameAttr.getValue());
4388}
4389
4390mlir::LogicalResult CIRToLLVMVTableAddrPointOpLowering::matchAndRewrite(
4391 cir::VTableAddrPointOp op, OpAdaptor adaptor,
4392 mlir::ConversionPatternRewriter &rewriter) const {
4393 const mlir::TypeConverter *converter = getTypeConverter();
4394 mlir::Type targetType = converter->convertType(op.getType());
4396 mlir::Type eltType;
4397 mlir::Value symAddr = getValueForVTableSymbol(op, rewriter, converter,
4398 op.getNameAttr(), eltType);
4399 if (!symAddr)
4400 return op.emitError() << "Unable to get value for vtable symbol";
4401
4403 0, op.getAddressPointAttr().getIndex(),
4404 op.getAddressPointAttr().getOffset()};
4405
4406 assert(eltType && "Shouldn't ever be missing an eltType here");
4407 mlir::LLVM::GEPNoWrapFlags inboundsNuw =
4408 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
4409 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(op, targetType, eltType,
4410 symAddr, offsets, inboundsNuw);
4411 return mlir::success();
4412}
4413
4414mlir::LogicalResult CIRToLLVMVTableGetVPtrOpLowering::matchAndRewrite(
4415 cir::VTableGetVPtrOp op, OpAdaptor adaptor,
4416 mlir::ConversionPatternRewriter &rewriter) const {
4417 // cir.vtable.get_vptr is equivalent to a bitcast from the source object
4418 // pointer to the vptr type. Since the LLVM dialect uses opaque pointers
4419 // we can just replace uses of this operation with the original pointer.
4420 mlir::Value srcVal = adaptor.getSrc();
4421 rewriter.replaceOp(op, srcVal);
4422 return mlir::success();
4423}
4424
4425mlir::LogicalResult CIRToLLVMVTableGetVirtualFnAddrOpLowering::matchAndRewrite(
4426 cir::VTableGetVirtualFnAddrOp op, OpAdaptor adaptor,
4427 mlir::ConversionPatternRewriter &rewriter) const {
4428 mlir::Type targetType = getTypeConverter()->convertType(op.getType());
4429 auto eltType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4432 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4433 op, targetType, eltType, adaptor.getVptr(), offsets,
4434 mlir::LLVM::GEPNoWrapFlags::inbounds);
4435 return mlir::success();
4436}
4437
4438mlir::LogicalResult CIRToLLVMVTTAddrPointOpLowering::matchAndRewrite(
4439 cir::VTTAddrPointOp op, OpAdaptor adaptor,
4440 mlir::ConversionPatternRewriter &rewriter) const {
4441 const mlir::Type resultType = getTypeConverter()->convertType(op.getType());
4443 mlir::Type eltType;
4444 mlir::Value llvmAddr = adaptor.getSymAddr();
4445
4446 if (op.getSymAddr()) {
4447 if (op.getOffset() == 0) {
4448 rewriter.replaceOp(op, {llvmAddr});
4449 return mlir::success();
4450 }
4451
4452 offsets.push_back(adaptor.getOffset());
4453 eltType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4454 } else {
4455 llvmAddr = getValueForVTableSymbol(op, rewriter, getTypeConverter(),
4456 op.getNameAttr(), eltType);
4457 assert(eltType && "Shouldn't ever be missing an eltType here");
4458 offsets.push_back(0);
4459 offsets.push_back(adaptor.getOffset());
4460 }
4461 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4462 op, resultType, eltType, llvmAddr, offsets,
4463 mlir::LLVM::GEPNoWrapFlags::inbounds);
4464 return mlir::success();
4465}
4466
4467mlir::LogicalResult CIRToLLVMVecCreateOpLowering::matchAndRewrite(
4468 cir::VecCreateOp op, OpAdaptor adaptor,
4469 mlir::ConversionPatternRewriter &rewriter) const {
4470 // Start with an 'undef' value for the vector. Then 'insertelement' for
4471 // each of the vector elements.
4472 const auto vecTy = mlir::cast<cir::VectorType>(op.getType());
4473 const mlir::Type llvmTy = typeConverter->convertType(vecTy);
4474 const mlir::Location loc = op.getLoc();
4475 mlir::Value result = mlir::LLVM::PoisonOp::create(rewriter, loc, llvmTy);
4476 assert(vecTy.getSize() == op.getElements().size() &&
4477 "cir.vec.create op count doesn't match vector type elements count");
4478
4479 for (uint64_t i = 0; i < vecTy.getSize(); ++i) {
4480 const mlir::Value indexValue =
4481 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), i);
4482 result = mlir::LLVM::InsertElementOp::create(
4483 rewriter, loc, result, adaptor.getElements()[i], indexValue);
4484 }
4485
4486 rewriter.replaceOp(op, result);
4487 return mlir::success();
4488}
4489
4490mlir::LogicalResult CIRToLLVMVecExtractOpLowering::matchAndRewrite(
4491 cir::VecExtractOp op, OpAdaptor adaptor,
4492 mlir::ConversionPatternRewriter &rewriter) const {
4493 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractElementOp>(
4494 op, adaptor.getVec(), adaptor.getIndex());
4495 return mlir::success();
4496}
4497
4498mlir::LogicalResult CIRToLLVMVecInsertOpLowering::matchAndRewrite(
4499 cir::VecInsertOp op, OpAdaptor adaptor,
4500 mlir::ConversionPatternRewriter &rewriter) const {
4501 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertElementOp>(
4502 op, adaptor.getVec(), adaptor.getValue(), adaptor.getIndex());
4503 return mlir::success();
4504}
4505
4506mlir::LogicalResult CIRToLLVMVecCmpOpLowering::matchAndRewrite(
4507 cir::VecCmpOp op, OpAdaptor adaptor,
4508 mlir::ConversionPatternRewriter &rewriter) const {
4509 mlir::Type elementType = elementTypeIfVector(op.getLhs().getType());
4510 mlir::Value bitResult;
4511 if (auto intType = mlir::dyn_cast<cir::IntType>(elementType)) {
4512 bitResult = mlir::LLVM::ICmpOp::create(
4513 rewriter, op.getLoc(),
4514 convertCmpKindToICmpPredicate(op.getKind(), intType.isSigned()),
4515 adaptor.getLhs(), adaptor.getRhs());
4516 } else if (mlir::isa<cir::FPTypeInterface>(elementType)) {
4517 bitResult = mlir::LLVM::FCmpOp::create(
4518 rewriter, op.getLoc(), convertCmpKindToFCmpPredicate(op.getKind()),
4519 adaptor.getLhs(), adaptor.getRhs());
4520 } else {
4521 return op.emitError() << "unsupported type for VecCmpOp: " << elementType;
4522 }
4523
4524 // LLVM IR vector comparison returns a vector of i1. This one-bit vector
4525 // must be sign-extended to the correct result type, unless a vector of i1 is
4526 // the type we need.
4527 if (cast<cir::IntType>(cast<cir::VectorType>(op.getType()).getElementType())
4528 .getWidth() > 1)
4529 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(
4530 op, typeConverter->convertType(op.getType()), bitResult);
4531 else
4532 rewriter.replaceOp(op, bitResult);
4533 return mlir::success();
4534}
4535
4536mlir::LogicalResult CIRToLLVMVecSplatOpLowering::matchAndRewrite(
4537 cir::VecSplatOp op, OpAdaptor adaptor,
4538 mlir::ConversionPatternRewriter &rewriter) const {
4539 // Vector splat can be implemented with an `insertelement` and a
4540 // `shufflevector`, which is better than an `insertelement` for each
4541 // element in the vector. Start with an undef vector. Insert the value into
4542 // the first element. Then use a `shufflevector` with a mask of all 0 to
4543 // fill out the entire vector with that value.
4544 cir::VectorType vecTy = op.getType();
4545 mlir::Type llvmTy = typeConverter->convertType(vecTy);
4546 mlir::Location loc = op.getLoc();
4547 mlir::Value poison = mlir::LLVM::PoisonOp::create(rewriter, loc, llvmTy);
4548
4549 mlir::Value elementValue = adaptor.getValue();
4550 if (elementValue.getDefiningOp<mlir::LLVM::PoisonOp>()) {
4551 // If the splat value is poison, then we can just use poison value
4552 // for the entire vector.
4553 rewriter.replaceOp(op, poison);
4554 return mlir::success();
4555 }
4556
4557 if (auto constValue = elementValue.getDefiningOp<mlir::LLVM::ConstantOp>()) {
4558 if (auto intAttr = dyn_cast<mlir::IntegerAttr>(constValue.getValue())) {
4559 mlir::DenseIntElementsAttr denseVec = mlir::DenseIntElementsAttr::get(
4560 mlir::cast<mlir::ShapedType>(llvmTy), intAttr.getValue());
4561 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
4562 op, denseVec.getType(), denseVec);
4563 return mlir::success();
4564 }
4565
4566 if (auto fpAttr = dyn_cast<mlir::FloatAttr>(constValue.getValue())) {
4567 mlir::DenseFPElementsAttr denseVec = mlir::DenseFPElementsAttr::get(
4568 mlir::cast<mlir::ShapedType>(llvmTy), fpAttr.getValue());
4569 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
4570 op, denseVec.getType(), denseVec);
4571 return mlir::success();
4572 }
4573 }
4574
4575 mlir::Value indexValue =
4576 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), 0);
4577 mlir::Value oneElement = mlir::LLVM::InsertElementOp::create(
4578 rewriter, loc, poison, elementValue, indexValue);
4579 SmallVector<int32_t> zeroValues(vecTy.getSize(), 0);
4580 rewriter.replaceOpWithNewOp<mlir::LLVM::ShuffleVectorOp>(op, oneElement,
4581 poison, zeroValues);
4582 return mlir::success();
4583}
4584
4585mlir::LogicalResult CIRToLLVMVecShuffleOpLowering::matchAndRewrite(
4586 cir::VecShuffleOp op, OpAdaptor adaptor,
4587 mlir::ConversionPatternRewriter &rewriter) const {
4588 // LLVM::ShuffleVectorOp takes an ArrayRef of int for the list of indices.
4589 // Convert the ClangIR ArrayAttr of IntAttr constants into a
4590 // SmallVector<int>.
4591 SmallVector<int, 8> indices;
4592 std::transform(
4593 op.getIndices().begin(), op.getIndices().end(),
4594 std::back_inserter(indices), [](mlir::Attribute intAttr) {
4595 return mlir::cast<cir::IntAttr>(intAttr).getValue().getSExtValue();
4596 });
4597 rewriter.replaceOpWithNewOp<mlir::LLVM::ShuffleVectorOp>(
4598 op, adaptor.getVec1(), adaptor.getVec2(), indices);
4599 return mlir::success();
4600}
4601
4602mlir::LogicalResult CIRToLLVMVecShuffleDynamicOpLowering::matchAndRewrite(
4603 cir::VecShuffleDynamicOp op, OpAdaptor adaptor,
4604 mlir::ConversionPatternRewriter &rewriter) const {
4605 // LLVM IR does not have an operation that corresponds to this form of
4606 // the built-in.
4607 // __builtin_shufflevector(V, I)
4608 // is implemented as this pseudocode, where the for loop is unrolled
4609 // and N is the number of elements:
4610 //
4611 // result = undef
4612 // maskbits = NextPowerOf2(N - 1)
4613 // masked = I & maskbits
4614 // for (i in 0 <= i < N)
4615 // result[i] = V[masked[i]]
4616 mlir::Location loc = op.getLoc();
4617 mlir::Value input = adaptor.getVec();
4618 mlir::Type llvmIndexVecType =
4619 getTypeConverter()->convertType(op.getIndices().getType());
4620 mlir::Type llvmIndexType = getTypeConverter()->convertType(
4621 elementTypeIfVector(op.getIndices().getType()));
4622 uint64_t numElements =
4623 mlir::cast<cir::VectorType>(op.getVec().getType()).getSize();
4624
4625 uint64_t maskBits = llvm::NextPowerOf2(numElements - 1) - 1;
4626 mlir::Value maskValue = mlir::LLVM::ConstantOp::create(
4627 rewriter, loc, llvmIndexType,
4628 rewriter.getIntegerAttr(llvmIndexType, maskBits));
4629 mlir::Value maskVector =
4630 mlir::LLVM::UndefOp::create(rewriter, loc, llvmIndexVecType);
4631
4632 for (uint64_t i = 0; i < numElements; ++i) {
4633 mlir::Value idxValue =
4634 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), i);
4635 maskVector = mlir::LLVM::InsertElementOp::create(rewriter, loc, maskVector,
4636 maskValue, idxValue);
4637 }
4638
4639 mlir::Value maskedIndices = mlir::LLVM::AndOp::create(
4640 rewriter, loc, llvmIndexVecType, adaptor.getIndices(), maskVector);
4641 mlir::Value result = mlir::LLVM::UndefOp::create(
4642 rewriter, loc, getTypeConverter()->convertType(op.getVec().getType()));
4643 for (uint64_t i = 0; i < numElements; ++i) {
4644 mlir::Value iValue =
4645 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), i);
4646 mlir::Value indexValue = mlir::LLVM::ExtractElementOp::create(
4647 rewriter, loc, maskedIndices, iValue);
4648 mlir::Value valueAtIndex =
4649 mlir::LLVM::ExtractElementOp::create(rewriter, loc, input, indexValue);
4650 result = mlir::LLVM::InsertElementOp::create(rewriter, loc, result,
4651 valueAtIndex, iValue);
4652 }
4653 rewriter.replaceOp(op, result);
4654 return mlir::success();
4655}
4656
4657mlir::LogicalResult CIRToLLVMVecTernaryOpLowering::matchAndRewrite(
4658 cir::VecTernaryOp op, OpAdaptor adaptor,
4659 mlir::ConversionPatternRewriter &rewriter) const {
4660 // Convert `cond` into a vector of i1, then use that in a `select` op.
4661 mlir::Value bitVec = mlir::LLVM::ICmpOp::create(
4662 rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, adaptor.getCond(),
4663 mlir::LLVM::ZeroOp::create(
4664 rewriter, op.getCond().getLoc(),
4665 typeConverter->convertType(op.getCond().getType())));
4666 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(
4667 op, bitVec, adaptor.getLhs(), adaptor.getRhs());
4668 return mlir::success();
4669}
4670
4671mlir::LogicalResult CIRToLLVMComplexAddOpLowering::matchAndRewrite(
4672 cir::ComplexAddOp op, OpAdaptor adaptor,
4673 mlir::ConversionPatternRewriter &rewriter) const {
4674 mlir::Value lhs = adaptor.getLhs();
4675 mlir::Value rhs = adaptor.getRhs();
4676 mlir::Location loc = op.getLoc();
4677
4678 auto complexType = mlir::cast<cir::ComplexType>(op.getLhs().getType());
4679 mlir::Type complexElemTy =
4680 getTypeConverter()->convertType(complexType.getElementType());
4681 auto lhsReal = mlir::LLVM::ExtractValueOp::create(
4682 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{0}));
4683 auto lhsImag = mlir::LLVM::ExtractValueOp::create(
4684 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{1}));
4685 auto rhsReal = mlir::LLVM::ExtractValueOp::create(
4686 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{0}));
4687 auto rhsImag = mlir::LLVM::ExtractValueOp::create(
4688 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{1}));
4689
4690 mlir::Value newReal;
4691 mlir::Value newImag;
4692 if (complexElemTy.isInteger()) {
4693 newReal = mlir::LLVM::AddOp::create(rewriter, loc, complexElemTy, lhsReal,
4694 rhsReal);
4695 newImag = mlir::LLVM::AddOp::create(rewriter, loc, complexElemTy, lhsImag,
4696 rhsImag);
4697 } else {
4700 newReal = mlir::LLVM::FAddOp::create(rewriter, loc, complexElemTy, lhsReal,
4701 rhsReal);
4702 newImag = mlir::LLVM::FAddOp::create(rewriter, loc, complexElemTy, lhsImag,
4703 rhsImag);
4704 }
4705
4706 mlir::Type complexLLVMTy =
4707 getTypeConverter()->convertType(op.getResult().getType());
4708 auto initialComplex =
4709 mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), complexLLVMTy);
4710
4711 auto realComplex = mlir::LLVM::InsertValueOp::create(
4712 rewriter, op->getLoc(), initialComplex, newReal, ArrayRef(int64_t{0}));
4713
4714 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
4715 op, realComplex, newImag, ArrayRef(int64_t{1}));
4716
4717 return mlir::success();
4718}
4719
4720mlir::LogicalResult CIRToLLVMComplexCreateOpLowering::matchAndRewrite(
4721 cir::ComplexCreateOp op, OpAdaptor adaptor,
4722 mlir::ConversionPatternRewriter &rewriter) const {
4723 mlir::Type complexLLVMTy =
4724 getTypeConverter()->convertType(op.getResult().getType());
4725 auto initialComplex =
4726 mlir::LLVM::UndefOp::create(rewriter, op->getLoc(), complexLLVMTy);
4727
4728 auto realComplex = mlir::LLVM::InsertValueOp::create(
4729 rewriter, op->getLoc(), initialComplex, adaptor.getReal(),
4730 ArrayRef(int64_t{0}));
4731
4732 auto complex = mlir::LLVM::InsertValueOp::create(
4733 rewriter, op->getLoc(), realComplex, adaptor.getImag(),
4734 ArrayRef(int64_t{1}));
4735
4736 rewriter.replaceOp(op, complex);
4737 return mlir::success();
4738}
4739
4740mlir::LogicalResult CIRToLLVMComplexRealOpLowering::matchAndRewrite(
4741 cir::ComplexRealOp op, OpAdaptor adaptor,
4742 mlir::ConversionPatternRewriter &rewriter) const {
4743 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4744 mlir::Value operand = adaptor.getOperand();
4745 if (mlir::isa<cir::ComplexType>(op.getOperand().getType())) {
4746 operand = mlir::LLVM::ExtractValueOp::create(
4747 rewriter, op.getLoc(), resultLLVMTy, operand,
4749 }
4750 rewriter.replaceOp(op, operand);
4751 return mlir::success();
4752}
4753
4754mlir::LogicalResult CIRToLLVMComplexSubOpLowering::matchAndRewrite(
4755 cir::ComplexSubOp op, OpAdaptor adaptor,
4756 mlir::ConversionPatternRewriter &rewriter) const {
4757 mlir::Value lhs = adaptor.getLhs();
4758 mlir::Value rhs = adaptor.getRhs();
4759 mlir::Location loc = op.getLoc();
4760
4761 auto complexType = mlir::cast<cir::ComplexType>(op.getLhs().getType());
4762 mlir::Type complexElemTy =
4763 getTypeConverter()->convertType(complexType.getElementType());
4764 auto lhsReal = mlir::LLVM::ExtractValueOp::create(
4765 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{0}));
4766 auto lhsImag = mlir::LLVM::ExtractValueOp::create(
4767 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{1}));
4768 auto rhsReal = mlir::LLVM::ExtractValueOp::create(
4769 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{0}));
4770 auto rhsImag = mlir::LLVM::ExtractValueOp::create(
4771 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{1}));
4772
4773 mlir::Value newReal;
4774 mlir::Value newImag;
4775 if (complexElemTy.isInteger()) {
4776 newReal = mlir::LLVM::SubOp::create(rewriter, loc, complexElemTy, lhsReal,
4777 rhsReal);
4778 newImag = mlir::LLVM::SubOp::create(rewriter, loc, complexElemTy, lhsImag,
4779 rhsImag);
4780 } else {
4783 newReal = mlir::LLVM::FSubOp::create(rewriter, loc, complexElemTy, lhsReal,
4784 rhsReal);
4785 newImag = mlir::LLVM::FSubOp::create(rewriter, loc, complexElemTy, lhsImag,
4786 rhsImag);
4787 }
4788
4789 mlir::Type complexLLVMTy =
4790 getTypeConverter()->convertType(op.getResult().getType());
4791 auto initialComplex =
4792 mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), complexLLVMTy);
4793
4794 auto realComplex = mlir::LLVM::InsertValueOp::create(
4795 rewriter, op->getLoc(), initialComplex, newReal, ArrayRef(int64_t{0}));
4796
4797 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
4798 op, realComplex, newImag, ArrayRef(int64_t{1}));
4799
4800 return mlir::success();
4801}
4802
4803mlir::LogicalResult CIRToLLVMComplexImagOpLowering::matchAndRewrite(
4804 cir::ComplexImagOp op, OpAdaptor adaptor,
4805 mlir::ConversionPatternRewriter &rewriter) const {
4806 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4807 mlir::Value operand = adaptor.getOperand();
4808 mlir::Location loc = op.getLoc();
4809
4810 if (mlir::isa<cir::ComplexType>(op.getOperand().getType())) {
4811 operand = mlir::LLVM::ExtractValueOp::create(
4812 rewriter, loc, resultLLVMTy, operand, llvm::ArrayRef<std::int64_t>{1});
4813 } else {
4814 mlir::TypedAttr zeroAttr = rewriter.getZeroAttr(resultLLVMTy);
4815 operand =
4816 mlir::LLVM::ConstantOp::create(rewriter, loc, resultLLVMTy, zeroAttr);
4817 }
4818
4819 rewriter.replaceOp(op, operand);
4820 return mlir::success();
4821}
4822
4823mlir::IntegerType computeBitfieldIntType(mlir::Type storageType,
4824 mlir::MLIRContext *context,
4825 unsigned &storageSize) {
4826 return TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
4827 .Case<cir::ArrayType>([&](cir::ArrayType atTy) {
4828 storageSize = atTy.getSize() * 8;
4829 return mlir::IntegerType::get(context, storageSize);
4830 })
4831 .Case<cir::IntType>([&](cir::IntType intTy) {
4832 storageSize = intTy.getWidth();
4833 return mlir::IntegerType::get(context, storageSize);
4834 })
4835 .Default([](mlir::Type) -> mlir::IntegerType {
4836 llvm_unreachable(
4837 "Either ArrayType or IntType expected for bitfields storage");
4838 });
4839}
4840
4841mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite(
4842 cir::SetBitfieldOp op, OpAdaptor adaptor,
4843 mlir::ConversionPatternRewriter &rewriter) const {
4844 mlir::OpBuilder::InsertionGuard guard(rewriter);
4845 rewriter.setInsertionPoint(op);
4846
4847 cir::BitfieldInfoAttr info = op.getBitfieldInfo();
4848 uint64_t size = info.getSize();
4849 uint64_t offset = info.getOffset();
4850 mlir::Type storageType = info.getStorageType();
4851 mlir::MLIRContext *context = storageType.getContext();
4852
4853 unsigned storageSize = 0;
4854
4855 mlir::IntegerType intType =
4856 computeBitfieldIntType(storageType, context, storageSize);
4857
4858 mlir::Value srcVal = createIntCast(rewriter, adaptor.getSrc(), intType);
4859 unsigned srcWidth = storageSize;
4860 mlir::Value resultVal = srcVal;
4861
4862 if (storageSize != size) {
4863 assert(storageSize > size && "Invalid bitfield size.");
4864
4865 mlir::Value val = mlir::LLVM::LoadOp::create(
4866 rewriter, op.getLoc(), intType, adaptor.getAddr(), op.getAlignment(),
4867 op.getIsVolatile());
4868
4869 srcVal =
4870 createAnd(rewriter, srcVal, llvm::APInt::getLowBitsSet(srcWidth, size));
4871 resultVal = srcVal;
4872 srcVal = createShL(rewriter, srcVal, offset);
4873
4874 // Mask out the original value.
4875 val = createAnd(rewriter, val,
4876 ~llvm::APInt::getBitsSet(srcWidth, offset, offset + size));
4877
4878 // Or together the unchanged values and the source value.
4879 srcVal = mlir::LLVM::OrOp::create(rewriter, op.getLoc(), val, srcVal);
4880 }
4881
4882 mlir::LLVM::StoreOp::create(rewriter, op.getLoc(), srcVal, adaptor.getAddr(),
4883 op.getAlignment(), op.getIsVolatile());
4884
4885 mlir::Type resultTy = getTypeConverter()->convertType(op.getType());
4886
4887 if (info.getIsSigned()) {
4888 assert(size <= storageSize);
4889 unsigned highBits = storageSize - size;
4890
4891 if (highBits) {
4892 resultVal = createShL(rewriter, resultVal, highBits);
4893 resultVal = createAShR(rewriter, resultVal, highBits);
4894 }
4895 }
4896
4897 resultVal = createIntCast(rewriter, resultVal,
4898 mlir::cast<mlir::IntegerType>(resultTy),
4899 info.getIsSigned());
4900
4901 rewriter.replaceOp(op, resultVal);
4902 return mlir::success();
4903}
4904
4905mlir::LogicalResult CIRToLLVMComplexImagPtrOpLowering::matchAndRewrite(
4906 cir::ComplexImagPtrOp op, OpAdaptor adaptor,
4907 mlir::ConversionPatternRewriter &rewriter) const {
4908 cir::PointerType operandTy = op.getOperand().getType();
4909 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4910 mlir::Type elementLLVMTy =
4911 getTypeConverter()->convertType(operandTy.getPointee());
4912
4913 mlir::LLVM::GEPArg gepIndices[2] = {{0}, {1}};
4914 mlir::LLVM::GEPNoWrapFlags inboundsNuw =
4915 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
4916 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4917 op, resultLLVMTy, elementLLVMTy, adaptor.getOperand(), gepIndices,
4918 inboundsNuw);
4919 return mlir::success();
4920}
4921
4922mlir::LogicalResult CIRToLLVMComplexRealPtrOpLowering::matchAndRewrite(
4923 cir::ComplexRealPtrOp op, OpAdaptor adaptor,
4924 mlir::ConversionPatternRewriter &rewriter) const {
4925 cir::PointerType operandTy = op.getOperand().getType();
4926 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4927 mlir::Type elementLLVMTy =
4928 getTypeConverter()->convertType(operandTy.getPointee());
4929
4930 mlir::LLVM::GEPArg gepIndices[2] = {0, 0};
4931 mlir::LLVM::GEPNoWrapFlags inboundsNuw =
4932 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
4933 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4934 op, resultLLVMTy, elementLLVMTy, adaptor.getOperand(), gepIndices,
4935 inboundsNuw);
4936 return mlir::success();
4937}
4938
4939mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
4940 cir::GetBitfieldOp op, OpAdaptor adaptor,
4941 mlir::ConversionPatternRewriter &rewriter) const {
4942
4943 mlir::OpBuilder::InsertionGuard guard(rewriter);
4944 rewriter.setInsertionPoint(op);
4945
4946 cir::BitfieldInfoAttr info = op.getBitfieldInfo();
4947 uint64_t size = info.getSize();
4948 uint64_t offset = info.getOffset();
4949 mlir::Type storageType = info.getStorageType();
4950 mlir::MLIRContext *context = storageType.getContext();
4951 unsigned storageSize = 0;
4952
4953 mlir::IntegerType intType =
4954 computeBitfieldIntType(storageType, context, storageSize);
4955
4956 mlir::Value val = mlir::LLVM::LoadOp::create(
4957 rewriter, op.getLoc(), intType, adaptor.getAddr(), op.getAlignment(),
4958 op.getIsVolatile());
4959 val = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), intType, val);
4960
4961 if (info.getIsSigned()) {
4962 assert(static_cast<unsigned>(offset + size) <= storageSize);
4963 unsigned highBits = storageSize - offset - size;
4964 val = createShL(rewriter, val, highBits);
4965 val = createAShR(rewriter, val, offset + highBits);
4966 } else {
4967 val = createLShR(rewriter, val, offset);
4968
4969 if (static_cast<unsigned>(offset) + size < storageSize)
4970 val = createAnd(rewriter, val,
4971 llvm::APInt::getLowBitsSet(storageSize, size));
4972 }
4973
4974 mlir::Type resTy = getTypeConverter()->convertType(op.getType());
4975 mlir::Value newOp = createIntCast(
4976 rewriter, val, mlir::cast<mlir::IntegerType>(resTy), info.getIsSigned());
4977 rewriter.replaceOp(op, newOp);
4978 return mlir::success();
4979}
4980
4981mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite(
4982 cir::InlineAsmOp op, OpAdaptor adaptor,
4983 mlir::ConversionPatternRewriter &rewriter) const {
4984 mlir::Type llResTy;
4985 if (op.getNumResults())
4986 llResTy = getTypeConverter()->convertType(op.getType(0));
4987
4988 cir::AsmFlavor dialect = op.getAsmFlavor();
4989 mlir::LLVM::AsmDialect llDialect = dialect == cir::AsmFlavor::x86_att
4990 ? mlir::LLVM::AsmDialect::AD_ATT
4991 : mlir::LLVM::AsmDialect::AD_Intel;
4992
4994 StringRef llvmAttrName = mlir::LLVM::InlineAsmOp::getElementTypeAttrName();
4995
4996 // this is for the lowering to LLVM from LLVM dialect. Otherwise, if we
4997 // don't have the result (i.e. void type as a result of operation), the
4998 // element type attribute will be attached to the whole instruction, but not
4999 // to the operand
5000 if (!op.getNumResults())
5001 opAttrs.push_back(mlir::Attribute());
5002
5003 SmallVector<mlir::Value> llvmOperands;
5004 SmallVector<mlir::Value> cirOperands;
5005 for (auto const &[llvmOp, cirOp] :
5006 zip(adaptor.getAsmOperands(), op.getAsmOperands())) {
5007 append_range(llvmOperands, llvmOp);
5008 append_range(cirOperands, cirOp);
5009 }
5010
5011 // so far we infer the llvm dialect element type attr from
5012 // CIR operand type.
5013 for (auto const &[cirOpAttr, cirOp] :
5014 zip(op.getOperandAttrs(), cirOperands)) {
5015 if (!mlir::isa<mlir::UnitAttr>(cirOpAttr)) {
5016 opAttrs.push_back(mlir::Attribute());
5017 continue;
5018 }
5019
5021 cir::PointerType typ = mlir::cast<cir::PointerType>(cirOp.getType());
5022 mlir::TypeAttr typAttr = mlir::TypeAttr::get(convertTypeForMemory(
5023 *getTypeConverter(), dataLayout, typ.getPointee()));
5024
5025 attrs.push_back(rewriter.getNamedAttr(llvmAttrName, typAttr));
5026 mlir::DictionaryAttr newDict = rewriter.getDictionaryAttr(attrs);
5027 opAttrs.push_back(newDict);
5028 }
5029
5030 rewriter.replaceOpWithNewOp<mlir::LLVM::InlineAsmOp>(
5031 op, llResTy, llvmOperands, op.getAsmStringAttr(), op.getConstraintsAttr(),
5032 op.getSideEffectsAttr(),
5033 /*is_align_stack*/ mlir::UnitAttr(),
5034 /*tail_call_kind*/
5035 mlir::LLVM::TailCallKindAttr::get(
5036 getContext(), mlir::LLVM::tailcallkind::TailCallKind::None),
5037 mlir::LLVM::AsmDialectAttr::get(getContext(), llDialect),
5038 rewriter.getArrayAttr(opAttrs));
5039
5040 return mlir::success();
5041}
5042
5043mlir::LogicalResult CIRToLLVMVAStartOpLowering::matchAndRewrite(
5044 cir::VAStartOp op, OpAdaptor adaptor,
5045 mlir::ConversionPatternRewriter &rewriter) const {
5046 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
5047 auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
5048 adaptor.getArgList());
5049 rewriter.replaceOpWithNewOp<mlir::LLVM::VaStartOp>(op, vaList);
5050 return mlir::success();
5051}
5052
5053mlir::LogicalResult CIRToLLVMVAEndOpLowering::matchAndRewrite(
5054 cir::VAEndOp op, OpAdaptor adaptor,
5055 mlir::ConversionPatternRewriter &rewriter) const {
5056 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
5057 auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
5058 adaptor.getArgList());
5059 rewriter.replaceOpWithNewOp<mlir::LLVM::VaEndOp>(op, vaList);
5060 return mlir::success();
5061}
5062
5063mlir::LogicalResult CIRToLLVMVACopyOpLowering::matchAndRewrite(
5064 cir::VACopyOp op, OpAdaptor adaptor,
5065 mlir::ConversionPatternRewriter &rewriter) const {
5066 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
5067 auto dstList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
5068 adaptor.getDstList());
5069 auto srcList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
5070 adaptor.getSrcList());
5071 rewriter.replaceOpWithNewOp<mlir::LLVM::VaCopyOp>(op, dstList, srcList);
5072 return mlir::success();
5073}
5074
5075mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
5076 cir::VAArgOp op, OpAdaptor adaptor,
5077 mlir::ConversionPatternRewriter &rewriter) const {
5079 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
5080 auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
5081 adaptor.getArgList());
5082
5083 mlir::Type llvmType =
5084 getTypeConverter()->convertType(op->getResultTypes().front());
5085 if (!llvmType)
5086 return mlir::failure();
5087
5088 rewriter.replaceOpWithNewOp<mlir::LLVM::VaArgOp>(op, llvmType, vaList);
5089 return mlir::success();
5090}
5091
5092mlir::LogicalResult CIRToLLVMLabelOpLowering::matchAndRewrite(
5093 cir::LabelOp op, OpAdaptor adaptor,
5094 mlir::ConversionPatternRewriter &rewriter) const {
5095 mlir::MLIRContext *ctx = rewriter.getContext();
5096 mlir::Block *block = op->getBlock();
5097 // A BlockTagOp cannot reside in the entry block. The address of the entry
5098 // block cannot be taken
5099 if (block->isEntryBlock()) {
5100 mlir::Block *newBlock =
5101 rewriter.splitBlock(op->getBlock(), mlir::Block::iterator(op));
5102 rewriter.setInsertionPointToEnd(block);
5103 mlir::LLVM::BrOp::create(rewriter, op.getLoc(), newBlock);
5104 }
5105 auto tagAttr =
5106 mlir::LLVM::BlockTagAttr::get(ctx, blockInfoAddr.getTagIndex());
5107 rewriter.setInsertionPoint(op);
5108
5109 auto blockTagOp =
5110 mlir::LLVM::BlockTagOp::create(rewriter, op->getLoc(), tagAttr);
5111 mlir::LLVM::LLVMFuncOp func = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
5112 auto blockInfoAttr =
5113 cir::BlockAddrInfoAttr::get(ctx, func.getSymName(), op.getLabel());
5114 blockInfoAddr.mapBlockTag(blockInfoAttr, blockTagOp);
5115 rewriter.eraseOp(op);
5116
5117 return mlir::success();
5118}
5119
5120mlir::LogicalResult CIRToLLVMBlockAddressOpLowering::matchAndRewrite(
5121 cir::BlockAddressOp op, OpAdaptor adaptor,
5122 mlir::ConversionPatternRewriter &rewriter) const {
5123 mlir::MLIRContext *ctx = rewriter.getContext();
5124
5125 mlir::LLVM::BlockTagOp matchLabel =
5126 blockInfoAddr.lookupBlockTag(op.getBlockAddrInfoAttr());
5127 mlir::LLVM::BlockTagAttr tagAttr;
5128 if (!matchLabel)
5129 // If the BlockTagOp has not been emitted yet, use a placeholder.
5130 // This will later be replaced with the correct tag index during
5131 // `resolveBlockAddressOp`.
5132 tagAttr = {};
5133 else
5134 tagAttr = matchLabel.getTag();
5135
5136 auto blkAddr = mlir::LLVM::BlockAddressAttr::get(
5137 rewriter.getContext(), op.getBlockAddrInfoAttr().getFunc(), tagAttr);
5138 rewriter.setInsertionPoint(op);
5139 auto newOp = mlir::LLVM::BlockAddressOp::create(
5140 rewriter, op.getLoc(), mlir::LLVM::LLVMPointerType::get(ctx), blkAddr);
5141 if (!matchLabel)
5142 blockInfoAddr.addUnresolvedBlockAddress(newOp, op.getBlockAddrInfoAttr());
5143 rewriter.replaceOp(op, newOp);
5144 return mlir::success();
5145}
5146
5147mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite(
5148 cir::IndirectBrOp op, OpAdaptor adaptor,
5149 mlir::ConversionPatternRewriter &rewriter) const {
5150
5151 mlir::Value targetAddr = adaptor.getAddr();
5152
5153 // If the poison attribute is set, use llvm.mlir.poison as the address.
5154 // This happens when the block has no predecessors and is essentially
5155 // unreachable. Do NOT erase the block argument directly, as that violates
5156 // the MLIR dialect conversion framework contract (the framework tracks block
5157 // arguments and will clean them up). A block with no predecessors simply
5158 // produces no PHI node.
5159 if (op.getPoison()) {
5160 auto llvmPtrType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
5161 targetAddr =
5162 mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), llvmPtrType);
5163 }
5164
5165 rewriter.replaceOpWithNewOp<mlir::LLVM::IndirectBrOp>(
5166 op, targetAddr, adaptor.getSuccOperands(), op.getSuccessors());
5167 return mlir::success();
5168}
5169
5170mlir::LogicalResult CIRToLLVMCpuIdOpLowering::matchAndRewrite(
5171 cir::CpuIdOp op, OpAdaptor adaptor,
5172 mlir::ConversionPatternRewriter &rewriter) const {
5173 mlir::Type i32Ty = rewriter.getI32Type();
5174 mlir::Type i64Ty = rewriter.getI64Type();
5175 mlir::Type i32PtrTy = mlir::LLVM::LLVMPointerType::get(i32Ty.getContext(), 0);
5176
5177 mlir::Type cpuidRetTy = mlir::LLVM::LLVMStructType::getLiteral(
5178 rewriter.getContext(), {i32Ty, i32Ty, i32Ty, i32Ty});
5179
5180 mlir::Value functionId = adaptor.getFunctionId();
5181 mlir::Value subFunctionId = adaptor.getSubFunctionId();
5182
5183 StringRef asmString, constraints;
5184 mlir::ModuleOp moduleOp = op->getParentOfType<mlir::ModuleOp>();
5185 llvm::Triple triple(
5186 mlir::cast<mlir::StringAttr>(
5187 moduleOp->getAttr(cir::CIRDialect::getTripleAttrName()))
5188 .getValue());
5189 if (triple.getArch() == llvm::Triple::x86) {
5190 asmString = "cpuid";
5191 constraints = "={ax},={bx},={cx},={dx},{ax},{cx}";
5192 } else {
5193 // x86-64 uses %rbx as the base register, so preserve it.
5194 asmString = "xchgq %rbx, ${1:q}\n"
5195 "cpuid\n"
5196 "xchgq %rbx, ${1:q}";
5197 constraints = "={ax},=r,={cx},={dx},0,2";
5198 }
5199
5200 mlir::Value inlineAsm =
5201 mlir::LLVM::InlineAsmOp::create(
5202 rewriter, op.getLoc(), cpuidRetTy, {functionId, subFunctionId},
5203 rewriter.getStringAttr(asmString),
5204 rewriter.getStringAttr(constraints),
5205 /*has_side_effects=*/mlir::UnitAttr{},
5206 /*is_align_stack=*/mlir::UnitAttr{},
5207 /*tail_call_kind=*/mlir::LLVM::TailCallKindAttr{},
5208 /*asm_dialect=*/mlir::LLVM::AsmDialectAttr{},
5209 /*operand_attrs=*/mlir::ArrayAttr{})
5210 .getResult(0);
5211
5212 mlir::Value basePtr = adaptor.getCpuInfo();
5213
5214 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
5215 unsigned alignment = layout.getTypeABIAlignment(i32Ty);
5216 for (unsigned i = 0; i < 4; i++) {
5217 mlir::Value extracted =
5218 mlir::LLVM::ExtractValueOp::create(rewriter, op.getLoc(), inlineAsm, i)
5219 .getResult();
5220 mlir::Value index = mlir::LLVM::ConstantOp::create(
5221 rewriter, op.getLoc(), i64Ty, rewriter.getI64IntegerAttr(i));
5222 llvm::SmallVector<mlir::Value, 1> gepIndices = {index};
5223 mlir::Value storePtr = mlir::LLVM::GEPOp::create(
5224 rewriter, op.getLoc(), i32PtrTy, i32Ty, basePtr,
5225 gepIndices, mlir::LLVM::GEPNoWrapFlags::none)
5226 .getResult();
5227 mlir::LLVM::StoreOp::create(rewriter, op.getLoc(), extracted, storePtr,
5228 alignment);
5229 }
5230
5231 rewriter.eraseOp(op);
5232 return mlir::success();
5233}
5234
5235mlir::LogicalResult CIRToLLVMMemChrOpLowering::matchAndRewrite(
5236 cir::MemChrOp op, OpAdaptor adaptor,
5237 mlir::ConversionPatternRewriter &rewriter) const {
5238 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
5239 mlir::Type srcTy = getTypeConverter()->convertType(op.getSrc().getType());
5240 mlir::Type patternTy =
5241 getTypeConverter()->convertType(op.getPattern().getType());
5242 mlir::Type lenTy = getTypeConverter()->convertType(op.getLen().getType());
5243 auto fnTy =
5244 mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {srcTy, patternTy, lenTy},
5245 /*isVarArg=*/false);
5246 llvm::StringRef fnName = "memchr";
5247
5248 mlir::Builder b(rewriter.getContext());
5249 mlir::NamedAttribute noundefAttr =
5250 b.getNamedAttr("llvm.noundef", b.getUnitAttr());
5251 mlir::DictionaryAttr noundefDict = mlir::DictionaryAttr::get(
5252 rewriter.getContext(), llvm::ArrayRef(noundefAttr));
5253 SmallVector<mlir::Attribute> argAttrVec(3, noundefDict);
5254 mlir::ArrayAttr argAttrs =
5255 mlir::ArrayAttr::get(rewriter.getContext(), argAttrVec);
5256
5257 createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy, argAttrs);
5258
5259 mlir::LLVM::CallOp newCall = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
5260 op, mlir::TypeRange{llvmPtrTy}, fnName,
5261 mlir::ValueRange{adaptor.getSrc(), adaptor.getPattern(),
5262 adaptor.getLen()});
5263 newCall.setArgAttrsAttr(argAttrs);
5264 return mlir::success();
5265}
5266
5267std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
5268 return std::make_unique<ConvertCIRToLLVMPass>();
5269}
5270
5271void populateCIRToLLVMPasses(mlir::OpPassManager &pm, bool enableOpenMP) {
5273 if (enableOpenMP)
5274 pm.addPass(mlir::omp::createMarkDeclareTargetPass());
5275 pm.addPass(createConvertCIRToLLVMPass());
5276 if (enableOpenMP)
5277 pm.addPass(mlir::omp::createHostOpFilteringPass());
5278}
5279
5280std::unique_ptr<llvm::Module>
5281lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx,
5282 bool enableOpenMP, StringRef mlirSaveTempsOutFile,
5283 llvm::vfs::FileSystem *fs) {
5284 llvm::TimeTraceScope scope("lower from CIR to LLVM directly");
5285
5286 mlir::MLIRContext *mlirCtx = mlirModule.getContext();
5287
5288 mlir::PassManager pm(mlirCtx);
5289 populateCIRToLLVMPasses(pm, enableOpenMP);
5290
5291 (void)mlir::applyPassManagerCLOptions(pm);
5292
5293 if (mlir::failed(pm.run(mlirModule))) {
5294 // FIXME: Handle any errors where they occurs and return a nullptr here.
5295 report_fatal_error(
5296 "The pass manager failed to lower CIR to LLVMIR dialect!");
5297 }
5298
5299 if (!mlirSaveTempsOutFile.empty()) {
5300 std::error_code ec;
5301 llvm::raw_fd_ostream out(mlirSaveTempsOutFile, ec);
5302 if (!ec)
5303 mlirModule->print(out);
5304 }
5305
5306 mlir::registerBuiltinDialectTranslation(*mlirCtx);
5307 mlir::registerLLVMDialectTranslation(*mlirCtx);
5308 mlir::registerOpenMPDialectTranslation(*mlirCtx);
5310
5311 llvm::TimeTraceScope translateScope("translateModuleToLLVMIR");
5312
5313 StringRef moduleName = mlirModule.getName().value_or("CIRToLLVMModule");
5314 std::unique_ptr<llvm::Module> llvmModule = mlir::translateModuleToLLVMIR(
5315 mlirModule, llvmCtx, moduleName, /*disableVerification=*/false, fs);
5316
5317 if (!llvmModule) {
5318 // FIXME: Handle any errors where they occurs and return a nullptr here.
5319 report_fatal_error("Lowering from LLVMIR dialect to llvm IR failed!");
5320 }
5321
5322 return llvmModule;
5323}
5324} // namespace direct
5325} // namespace cir
static bool isUnsigned(SValBuilder &SVB, NonLoc Value)
static llvm::StringRef getLinkageAttrNameString()
Returns the name used for the linkage attribute.
mlir::Value createLShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs)
mlir::Value createShL(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs)
mlir::Type adjustGlobalTypeForInit(mlir::Type llvmType, mlir::Attribute init, const mlir::TypeConverter &converter, const mlir::DataLayout &dataLayout)
Adjust llvmType (the converted type of init) to the concrete LLVM type a global constant initialized ...
mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs)
mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs, const llvm::APInt &rhs)
std::optional< mlir::Attribute > lowerConstArrayAttr(cir::ConstArrayAttr constArr, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp={})
std::optional< mlir::Attribute > lowerConstRecordAttr(cir::ConstRecordAttr constRecord, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp={})
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
static bool isVector(QualType QT, QualType ElementType)
This helper function returns true if QT is a vector type that has element type ElementType.
__DEVICE__ void * memset(void *__a, int __b, size_t __c)
CIRAttrToValue(mlir::Operation *parentOp, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *converter, LLVMBlockAddressInfo *blockInfoAddr=nullptr)
mlir::Attribute visit(mlir::Attribute attr)
mlir::Attribute visitCirAttr(cir::FPAttr attr)
mlir::Attribute visitCirAttr(cir::BoolAttr attr)
GlobalInitAttrRewriter(mlir::Type type, mlir::ConversionPatternRewriter &rewriter)
mlir::Attribute visitCirAttr(cir::IntAttr attr)
static mlir::LLVM::CConv convertCallingConv(cir::CallingConv callingConv)
static mlir::LogicalResult lowerIncDecOp(CIROp op, typename CIROp::Adaptor adaptor, mlir::ConversionPatternRewriter &rewriter)
void populateCIRToLLVMPasses(mlir::OpPassManager &pm, bool enableOpenMP)
Adds passes that fully lower CIR to the LLVMIR dialect.
static mlir::LLVM::AtomicBinOp getLLVMAtomicBinOp(cir::AtomicFetchKind k, bool isInt, bool isSignedInt)
static mlir::LLVM::ICmpPredicate convertCmpKindToICmpPredicate(cir::CmpOpKind kind, bool isSigned)
Convert from a CIR comparison kind to an LLVM IR integral comparison kind.
void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow, cir::SideEffect sideEffect, mlir::LLVM::MemoryEffectsAttr &memoryEffect, bool &noUnwind, bool &willReturn, bool &noReturn)
static bool isBulkLowerableConstArrayBaseElement(mlir::Type baseElemTy)
static mlir::LLVM::IntegerOverflowFlags intOverflowFlag(BinOp op)
static mlir::Value castBitIntMemoryStorage(mlir::ConversionPatternRewriter &rewriter, const mlir::DataLayout &dataLayout, cir::IntType intTy, mlir::Value value, bool toMemory)
Cast a _BitInt(N) value between its literal width iN and its padded in-memory storage iM (sign/zero-e...
static bool isSplitStorageBitInt(cir::IntType ty, const mlir::DataLayout &dataLayout)
A _BitInt(N) whose padded storage integer iM has a larger alloc size than its M/8 store size is laid ...
void createLLVMFuncOpIfNotExist(mlir::ConversionPatternRewriter &rewriter, mlir::Operation *srcOp, llvm::StringRef fnName, mlir::Type fnTy, mlir::ArrayAttr argAttrs=nullptr, mlir::ArrayAttr resAttrs=nullptr)
static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter, mlir::Value llvmSrc, mlir::Type llvmDstIntTy, bool isUnsigned, uint64_t cirSrcWidth, uint64_t cirDstIntWidth)
static mlir::Value emitFromMemory(mlir::ConversionPatternRewriter &rewriter, mlir::DataLayout const &dataLayout, cir::LoadOp op, mlir::Value value)
Emits the value from memory as expected by its users.
mlir::IntegerType computeBitfieldIntType(mlir::Type storageType, mlir::MLIRContext *context, unsigned &storageSize)
static mlir::LLVM::CallIntrinsicOp createCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter, mlir::Location loc, const llvm::Twine &intrinsicName, mlir::Type resultTy, mlir::ValueRange operands)
static mlir::LogicalResult lowerMinMaxOp(CIROp op, typename CIROp::Adaptor adaptor, mlir::ConversionPatternRewriter &rewriter)
static mlir::LLVM::LLVMStructType getLLVMLandingPadStructTy(mlir::ConversionPatternRewriter &rewriter)
static mlir::ArrayAttr convertTypedArgAttrs(mlir::ArrayAttr argAttrs, const mlir::TypeConverter &converter, mlir::MLIRContext *ctx)
The llvm.byval, llvm.sret, and llvm.byref argument attributes carry the pointee type as a TypeAttr.
static void lowerCallAttributes(cir::CIRCallOpInterface op, const mlir::TypeConverter &converter, SmallVectorImpl< mlir::NamedAttribute > &result)
static mlir::Value convertToIndexTy(mlir::ConversionPatternRewriter &rewriter, mlir::ModuleOp mod, mlir::Value index, mlir::Type baseTy, cir::IntType strideTy)
static mlir::LogicalResult lowerIntBinaryOp(CIROp op, mlir::Value lhs, mlir::Value rhs, mlir::ConversionPatternRewriter &rewriter)
Lower an integer Div/Rem op to its signed or unsigned LLVM counterpart.
static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter, mlir::Operation *op, const llvm::Twine &intrinsicName, mlir::Type resultTy, mlir::ValueRange operands)
static unsigned getBitIntMemoryStorageBits(cir::IntType ty, const mlir::DataLayout &dataLayout)
In-memory storage width in bits for a _BitInt(N): N rounded up to the type's ABI alignment.
std::unique_ptr< llvm::Module > lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, llvm::LLVMContext &llvmCtx, bool enableOpenMP, llvm::StringRef mlirSaveTempsOutFile={}, llvm::vfs::FileSystem *fs=nullptr)
static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, mlir::DataLayout &dataLayout)
static mlir::LLVM::AtomicOrdering getLLVMMemOrder(std::optional< cir::MemOrder > memorder)
std::unique_ptr< mlir::Pass > createConvertCIRToLLVMPass()
Create a pass that fully lowers CIR to the LLVMIR dialect.
static llvm::StringRef getLLVMSyncScope(cir::SyncScopeKind syncScope)
static mlir::LogicalResult lowerSaturatableArithOp(CIROp op, mlir::Value lhs, mlir::Value rhs, mlir::ConversionPatternRewriter &rewriter)
Lower an arithmetic op that supports saturation, overflow flags, and an FP Lower an integer Add/Sub o...
static uint64_t getMemoryFallbackAlignment(mlir::Type cirType, mlir::Type llvmMemType, const mlir::DataLayout &dataLayout)
Alignment to use for a memory access whose op carries no explicit alignment.
static mlir::Type getConstArrayBaseElementType(mlir::Type ty)
static mlir::LLVM::FCmpPredicate convertCmpKindToFCmpPredicate(cir::CmpOpKind kind)
Convert from a CIR comparison kind to an LLVM IR floating-point comparison kind.
static mlir::LogicalResult rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *converter, mlir::SymbolTableCollection &symbolTables, mlir::FlatSymbolRefAttr calleeAttr, mlir::Block *continueBlock=nullptr, mlir::Block *landingPadBlock=nullptr)
static mlir::LLVM::Visibility lowerCIRVisibilityToLLVMVisibility(cir::VisibilityKind visibilityKind)
static uint64_t getTypeSize(mlir::Type type, mlir::Operation &op)
static llvm::StringLiteral getLLVMBinopForPostAtomic(cir::AtomicFetchKind k, bool isInt)
mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage)
static void buildCtorDtorList(mlir::ModuleOp module, StringRef globalXtorName, StringRef llvmXtorName, llvm::function_ref< std::pair< StringRef, int >(mlir::Attribute)> createXtor)
static mlir::LogicalResult lowerBinOpOverflow(OpTy op, typename OpTy::Adaptor adaptor, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *typeConverter, llvm::StringRef opStr)
Shared lowering logic for checked binary arithmetic overflow operations.
static mlir::Type convertTypeForMemory(const mlir::TypeConverter &converter, mlir::DataLayout const &dataLayout, mlir::Type type)
Given a type convertor and a data layout, convert the given type to a type that is suitable for memor...
static mlir::Value createIntCast(mlir::OpBuilder &bld, mlir::Value src, mlir::IntegerType dstTy, bool isSigned=false)
static mlir::LLVM::IntegerOverflowFlags nswFlag(bool nsw)
static mlir::Value getValueForVTableSymbol(mlir::Operation *op, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *converter, mlir::FlatSymbolRefAttr nameAttr, mlir::Type &eltType)
static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter, mlir::DataLayout const &dataLayout, mlir::Type origType, mlir::Value value)
Emits a value to memory with the expected scalar type.
mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, const mlir::Attribute attr, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *converter, LLVMBlockAddressInfo *blockInfoAddr)
Switches on the type of attribute and calls the appropriate conversion.
static bool isIntTypeUnsigned(mlir::Type type)
void collectUnreachable(mlir::Operation *parent, llvm::SmallVectorImpl< mlir::Operation * > &ops)
Collect ops in blocks that are unreachable from their region's entry, appending them to ops.
llvm::Type * convertTypeForMemory(CodeGenModule &CGM, QualType T)
const internal::VariadicAllOfMatcher< Attr > attr
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
const AstTypeMatcher< ComplexType > complexType
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.
unsigned long uint64_t
long int64_t
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
void populateCIRPreLoweringPasses(mlir::OpPassManager &pm)
void registerCIRDialectTranslation(mlir::MLIRContext &context)
char __ovld __cnfn clz(char)
Returns the number of leading 0-bits in x, starting at the most significant bit position.
char __ovld __cnfn ctz(char)
Returns the count of trailing 0-bits in x.
float __ovld __cnfn sign(float)
Returns 1.0 if x > 0, -0.0 if x = -0.0, +0.0 if x = +0.0, or -1.0 if x < 0.
float __ovld __cnfn length(float)
Return the length of vector p, i.e., sqrt(p.x2 + p.y 2 + ...)
char __ovld __cnfn select(char, char, char)
For each component of a vector type, result[i] = if MSB of c[i] is set ?
__packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 __packed_splat2 __packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 uint32_t
static bool dataMemberType()
static bool addressSpace()
static bool globalViewIntLowering()
static bool opAllocaAnnotations()
static bool opLoadStoreTbaa()
static bool optInfoAttr()
static bool opFuncExtraAttrs()
static bool isPPC_FP128Ty()
static bool vaArgABILowering()
static bool fpConstraints()
static bool intrinsicElementTypeSupport()
static bool lowerModeOptLevel()
static bool opCallCallConv()
static bool aggValueSlotVolatile()
static bool fastMathFlags()
static bool llvmLoweringPtrDiffConsidersPointee()
static bool atomicSyncScopeID()
static bool opFuncMultipleReturnVals()
void collectGlobalAnnotations(mlir::ModuleOp module)
Collect (symbol_name, annotations, loc) from cir.func and cir.global ops before the conversion runs (...
StringRef getDescription() const override
StringRef getArgument() const override
void getDependentDialects(mlir::DialectRegistry &registry) const override
void resolveBlockAddressOp(LLVMBlockAddressInfo &blockInfoAddr)
void buildGlobalAnnotationsVar(mlir::ModuleOp module)
Emit @llvm.global.annotations and supporting string/args constants from the previously-collected anno...
void processCIRAttrs(mlir::ModuleOp module)
mlir::LLVM::BlockTagOp lookupBlockTag(cir::BlockAddrInfoAttr info) const
Definition LowerToLLVM.h:53
llvm::DenseMap< mlir::LLVM::BlockAddressOp, cir::BlockAddrInfoAttr > & getUnresolvedBlockAddress()
Definition LowerToLLVM.h:66