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