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