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