Thanks to visit codestin.com
Credit goes to clang.llvm.org

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