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