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