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