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

Follow Lee on X/Twitter - Father, Husband, Serial builder creating AI, crypto, games & web tools. We are friends :) AI Will Come To Life!

Check out: eBank.nz (Art Generator) | Netwrck.com (AI Tools) | Text-Generator.io (AI API) | BitBank.nz (Crypto AI) | ReadingTime (Kids Reading) | RewordGame | BigMultiplayerChess | WebFiddle | How.nz | Helix AI Assistant