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