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