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