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