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