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