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