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