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