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