clang 23.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 <array>
16#include <optional>
17
18#include "mlir/Conversion/LLVMCommon/TypeConverter.h"
19#include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h"
20#include "mlir/Dialect/DLTI/DLTI.h"
21#include "mlir/Dialect/Func/IR/FuncOps.h"
22#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
23#include "mlir/Dialect/LLVMIR/LLVMTypes.h"
24#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
25#include "mlir/Dialect/OpenMP/Transforms/Passes.h"
26#include "mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h"
27#include "mlir/IR/BuiltinAttributes.h"
28#include "mlir/IR/BuiltinDialect.h"
29#include "mlir/IR/BuiltinOps.h"
30#include "mlir/IR/Location.h"
31#include "mlir/IR/Types.h"
32#include "mlir/Pass/Pass.h"
33#include "mlir/Pass/PassManager.h"
34#include "mlir/Support/LLVM.h"
35#include "mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h"
36#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
37#include "mlir/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.h"
38#include "mlir/Target/LLVMIR/Export.h"
39#include "mlir/Transforms/DialectConversion.h"
47#include "clang/CIR/Passes.h"
48#include "llvm/ADT/MapVector.h"
49#include "llvm/ADT/StringMap.h"
50#include "llvm/ADT/TypeSwitch.h"
51#include "llvm/IR/Module.h"
52#include "llvm/Support/Casting.h"
53#include "llvm/Support/ErrorHandling.h"
54#include "llvm/Support/TimeProfiler.h"
55#include "llvm/Support/VirtualFileSystem.h"
56#include "llvm/Support/raw_ostream.h"
57
58using namespace cir;
59using namespace llvm;
60
61namespace cir {
62namespace direct {
63
64//===----------------------------------------------------------------------===//
65// Helper Methods
66//===----------------------------------------------------------------------===//
67
68namespace {
69/// If the given type is a vector type, return the vector's element type.
70/// Otherwise return the given type unchanged.
71mlir::Type elementTypeIfVector(mlir::Type type) {
72 return llvm::TypeSwitch<mlir::Type, mlir::Type>(type)
73 .Case<cir::VectorType, mlir::VectorType>(
74 [](auto p) { return p.getElementType(); })
75 .Default([](mlir::Type p) { return p; });
76}
77} // namespace
78
79/// Given a type convertor and a data layout, convert the given type to a type
80/// that is suitable for memory operations. For example, this can be used to
81/// lower cir.bool accesses to i8.
82static mlir::Type convertTypeForMemory(const mlir::TypeConverter &converter,
83 mlir::DataLayout const &dataLayout,
84 mlir::Type type) {
85 // TODO(cir): Handle other types similarly to clang's codegen
86 // convertTypeForMemory
87 if (isa<cir::BoolType>(type)) {
88 return mlir::IntegerType::get(type.getContext(),
89 dataLayout.getTypeSizeInBits(type));
90 }
91
92 return converter.convertType(type);
93}
94
95static mlir::Value createIntCast(mlir::OpBuilder &bld, mlir::Value src,
96 mlir::IntegerType dstTy,
97 bool isSigned = false) {
98 mlir::Type srcTy = src.getType();
99 assert(mlir::isa<mlir::IntegerType>(srcTy));
100
101 unsigned srcWidth = mlir::cast<mlir::IntegerType>(srcTy).getWidth();
102 unsigned dstWidth = mlir::cast<mlir::IntegerType>(dstTy).getWidth();
103 mlir::Location loc = src.getLoc();
104
105 if (dstWidth > srcWidth && isSigned)
106 return mlir::LLVM::SExtOp::create(bld, loc, dstTy, src);
107 if (dstWidth > srcWidth)
108 return mlir::LLVM::ZExtOp::create(bld, loc, dstTy, src);
109 if (dstWidth < srcWidth)
110 return mlir::LLVM::TruncOp::create(bld, loc, dstTy, src);
111 return mlir::LLVM::BitcastOp::create(bld, loc, dstTy, src);
112}
113
114static mlir::LLVM::Visibility
115lowerCIRVisibilityToLLVMVisibility(cir::VisibilityKind visibilityKind) {
116 switch (visibilityKind) {
117 case cir::VisibilityKind::Default:
118 return ::mlir::LLVM::Visibility::Default;
119 case cir::VisibilityKind::Hidden:
120 return ::mlir::LLVM::Visibility::Hidden;
121 case cir::VisibilityKind::Protected:
122 return ::mlir::LLVM::Visibility::Protected;
123 }
124}
125
126/// Emits the value from memory as expected by its users. Should be called when
127/// the memory represetnation of a CIR type is not equal to its scalar
128/// representation.
129static mlir::Value emitFromMemory(mlir::ConversionPatternRewriter &rewriter,
130 mlir::DataLayout const &dataLayout,
131 cir::LoadOp op, mlir::Value value) {
132
133 // TODO(cir): Handle other types similarly to clang's codegen EmitFromMemory
134 if (auto boolTy = mlir::dyn_cast<cir::BoolType>(op.getType())) {
135 // Create a cast value from specified size in datalayout to i1
136 assert(value.getType().isInteger(dataLayout.getTypeSizeInBits(boolTy)));
137 return createIntCast(rewriter, value, rewriter.getI1Type());
138 }
139
140 return value;
141}
142
143/// Emits a value to memory with the expected scalar type. Should be called when
144/// the memory represetnation of a CIR type is not equal to its scalar
145/// representation.
146static mlir::Value emitToMemory(mlir::ConversionPatternRewriter &rewriter,
147 mlir::DataLayout const &dataLayout,
148 mlir::Type origType, mlir::Value value) {
149
150 // TODO(cir): Handle other types similarly to clang's codegen EmitToMemory
151 if (auto boolTy = mlir::dyn_cast<cir::BoolType>(origType)) {
152 // Create zext of value from i1 to i8
153 mlir::IntegerType memType =
154 rewriter.getIntegerType(dataLayout.getTypeSizeInBits(boolTy));
155 return createIntCast(rewriter, value, memType);
156 }
157
158 return value;
159}
160
161mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage) {
162 using CIR = cir::GlobalLinkageKind;
163 using LLVM = mlir::LLVM::Linkage;
164
165 switch (linkage) {
166 case CIR::AppendingLinkage:
167 return LLVM::Appending;
168 case CIR::AvailableExternallyLinkage:
169 return LLVM::AvailableExternally;
170 case CIR::CommonLinkage:
171 return LLVM::Common;
172 case CIR::ExternalLinkage:
173 return LLVM::External;
174 case CIR::ExternalWeakLinkage:
175 return LLVM::ExternWeak;
176 case CIR::InternalLinkage:
177 return LLVM::Internal;
178 case CIR::LinkOnceAnyLinkage:
179 return LLVM::Linkonce;
180 case CIR::LinkOnceODRLinkage:
181 return LLVM::LinkonceODR;
182 case CIR::PrivateLinkage:
183 return LLVM::Private;
184 case CIR::WeakAnyLinkage:
185 return LLVM::Weak;
186 case CIR::WeakODRLinkage:
187 return LLVM::WeakODR;
188 };
189 llvm_unreachable("Unknown CIR linkage type");
190}
191
192static mlir::LLVM::CConv convertCallingConv(cir::CallingConv callingConv) {
193 using CIR = cir::CallingConv;
194 using LLVM = mlir::LLVM::CConv;
195
196 switch (callingConv) {
197 case CIR::C:
198 return LLVM::C;
199 case CIR::SpirKernel:
200 return LLVM::SPIR_KERNEL;
201 case CIR::SpirFunction:
202 return LLVM::SPIR_FUNC;
203 case CIR::PTXKernel:
204 return LLVM::PTX_Kernel;
205 case CIR::AMDGPUKernel:
206 return LLVM::AMDGPU_KERNEL;
207 }
208 llvm_unreachable("Unknown calling convention");
209}
210
211mlir::LogicalResult CIRToLLVMCopyOpLowering::matchAndRewrite(
212 cir::CopyOp op, OpAdaptor adaptor,
213 mlir::ConversionPatternRewriter &rewriter) const {
214 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
215 const mlir::Value length = mlir::LLVM::ConstantOp::create(
216 rewriter, op.getLoc(), rewriter.getI64Type(),
217 op.getCopySizeInBytes(layout));
219 rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
220 op, adaptor.getDst(), adaptor.getSrc(), length, op.getIsVolatile());
221 return mlir::success();
222}
223
224mlir::LogicalResult CIRToLLVMMemCpyOpLowering::matchAndRewrite(
225 cir::MemCpyOp op, OpAdaptor adaptor,
226 mlir::ConversionPatternRewriter &rewriter) const {
227 rewriter.replaceOpWithNewOp<mlir::LLVM::MemcpyOp>(
228 op, adaptor.getDst(), adaptor.getSrc(), adaptor.getLen(),
229 /*isVolatile=*/false);
230 return mlir::success();
231}
232
233mlir::LogicalResult CIRToLLVMMemMoveOpLowering::matchAndRewrite(
234 cir::MemMoveOp op, OpAdaptor adaptor,
235 mlir::ConversionPatternRewriter &rewriter) const {
236 rewriter.replaceOpWithNewOp<mlir::LLVM::MemmoveOp>(
237 op, adaptor.getDst(), adaptor.getSrc(), adaptor.getLen(),
238 /*isVolatile=*/false);
239 return mlir::success();
240}
241
242mlir::LogicalResult CIRToLLVMMemSetOpLowering::matchAndRewrite(
243 cir::MemSetOp op, OpAdaptor adaptor,
244 mlir::ConversionPatternRewriter &rewriter) const {
245
246 auto memset = rewriter.replaceOpWithNewOp<mlir::LLVM::MemsetOp>(
247 op, adaptor.getDst(), adaptor.getVal(), adaptor.getLen(),
248 /*isVolatile=*/false);
249
250 if (op.getAlignmentAttr()) {
251 // Construct a list full of empty attributes.
252 llvm::SmallVector<mlir::Attribute> attrs{memset.getNumOperands(),
253 rewriter.getDictionaryAttr({})};
254 llvm::SmallVector<mlir::NamedAttribute> destAttrs;
255 destAttrs.push_back(
256 {mlir::LLVM::LLVMDialect::getAlignAttrName(), op.getAlignmentAttr()});
257 attrs[memset.odsIndex_dst] = rewriter.getDictionaryAttr(destAttrs);
258
259 auto arrayAttr = rewriter.getArrayAttr(attrs);
260 memset.setArgAttrsAttr(arrayAttr);
261 }
262
263 return mlir::success();
264}
265
266static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter,
267 mlir::Value llvmSrc, mlir::Type llvmDstIntTy,
268 bool isUnsigned, uint64_t cirSrcWidth,
269 uint64_t cirDstIntWidth) {
270 if (cirSrcWidth == cirDstIntWidth)
271 return llvmSrc;
272
273 auto loc = llvmSrc.getLoc();
274 if (cirSrcWidth < cirDstIntWidth) {
275 if (isUnsigned)
276 return mlir::LLVM::ZExtOp::create(rewriter, loc, llvmDstIntTy, llvmSrc);
277 return mlir::LLVM::SExtOp::create(rewriter, loc, llvmDstIntTy, llvmSrc);
278 }
279
280 // Otherwise truncate
281 return mlir::LLVM::TruncOp::create(rewriter, loc, llvmDstIntTy, llvmSrc);
282}
283
285public:
286 CIRAttrToValue(mlir::Operation *parentOp,
287 mlir::ConversionPatternRewriter &rewriter,
288 const mlir::TypeConverter *converter)
289 : parentOp(parentOp), rewriter(rewriter), converter(converter) {}
290
291#define GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS
292#include "clang/CIR/Dialect/IR/CIRLowering.inc"
293#undef GET_CIR_ATTR_TO_VALUE_VISITOR_DECLS
294
295private:
296 mlir::Operation *parentOp;
297 mlir::ConversionPatternRewriter &rewriter;
298 const mlir::TypeConverter *converter;
299};
300
301/// Switches on the type of attribute and calls the appropriate conversion.
302mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp,
303 const mlir::Attribute attr,
304 mlir::ConversionPatternRewriter &rewriter,
305 const mlir::TypeConverter *converter) {
306 CIRAttrToValue valueConverter(parentOp, rewriter, converter);
307 mlir::Value value = valueConverter.visit(attr);
308 if (!value)
309 llvm_unreachable("unhandled attribute type");
310 return value;
311}
312
313void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
314 cir::SideEffect sideEffect,
315 mlir::LLVM::MemoryEffectsAttr &memoryEffect,
316 bool &noUnwind, bool &willReturn,
317 bool &noReturn) {
318 using mlir::LLVM::ModRefInfo;
319
320 switch (sideEffect) {
321 case cir::SideEffect::All:
322 memoryEffect = {};
323 noUnwind = isNothrow;
324 willReturn = false;
325 break;
326
327 case cir::SideEffect::Pure:
328 memoryEffect = mlir::LLVM::MemoryEffectsAttr::get(
329 callOp->getContext(), /*other=*/ModRefInfo::Ref,
330 /*argMem=*/ModRefInfo::Ref,
331 /*inaccessibleMem=*/ModRefInfo::Ref,
332 /*errnoMem=*/ModRefInfo::Ref,
333 /*targetMem0=*/ModRefInfo::Ref,
334 /*targetMem1=*/ModRefInfo::Ref);
335 noUnwind = true;
336 willReturn = true;
337 break;
338
339 case cir::SideEffect::Const:
340 memoryEffect = mlir::LLVM::MemoryEffectsAttr::get(
341 callOp->getContext(), /*other=*/ModRefInfo::NoModRef,
342 /*argMem=*/ModRefInfo::NoModRef,
343 /*inaccessibleMem=*/ModRefInfo::NoModRef,
344 /*errnoMem=*/ModRefInfo::NoModRef,
345 /*targetMem0=*/ModRefInfo::NoModRef,
346 /*targetMem1=*/ModRefInfo::NoModRef);
347 noUnwind = true;
348 willReturn = true;
349 break;
350 }
351
352 noReturn = callOp->hasAttr(CIRDialect::getNoReturnAttrName());
353}
354
355static mlir::LLVM::CallIntrinsicOp
356createCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter,
357 mlir::Location loc, const llvm::Twine &intrinsicName,
358 mlir::Type resultTy, mlir::ValueRange operands) {
359 auto intrinsicNameAttr =
360 mlir::StringAttr::get(rewriter.getContext(), intrinsicName);
361 // CallIntrinsicOp has distinct void / single-result create overloads.
362 if (resultTy)
363 return mlir::LLVM::CallIntrinsicOp::create(rewriter, loc, resultTy,
364 intrinsicNameAttr, operands);
365 return mlir::LLVM::CallIntrinsicOp::create(rewriter, loc, intrinsicNameAttr,
366 operands);
367}
368
369static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(
370 mlir::ConversionPatternRewriter &rewriter, mlir::Operation *op,
371 const llvm::Twine &intrinsicName, mlir::Type resultTy,
372 mlir::ValueRange operands) {
373 mlir::LLVM::CallIntrinsicOp callIntrinOp = createCallLLVMIntrinsicOp(
374 rewriter, op->getLoc(), intrinsicName, resultTy, operands);
375 rewriter.replaceOp(op, callIntrinOp.getOperation());
376 return callIntrinOp;
377}
378
379mlir::LogicalResult CIRToLLVMLLVMIntrinsicCallOpLowering::matchAndRewrite(
380 cir::LLVMIntrinsicCallOp op, OpAdaptor adaptor,
381 mlir::ConversionPatternRewriter &rewriter) const {
382 // Result is Optional on the op, so void intrinsics have zero
383 // results; leave llvmResTy null in that case.
384 mlir::Type llvmResTy;
385 if (op->getNumResults() != 0) {
386 llvmResTy = getTypeConverter()->convertType(op->getResultTypes()[0]);
387 if (!llvmResTy)
388 return op.emitError("expected LLVM result type");
389 }
390 StringRef name = op.getIntrinsicName();
391
392 // Some LLVM intrinsics require ElementType attribute to be attached to
393 // the argument of pointer type. That prevents us from generating LLVM IR
394 // because from LLVM dialect, we have LLVM IR like the below which fails
395 // LLVM IR verification.
396 // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr %2)
397 // The expected LLVM IR should be like
398 // %3 = call i64 @llvm.aarch64.ldxr.p0(ptr elementtype(i32) %2)
399 // TODO(cir): MLIR LLVM dialect should handle this part as CIR has no way
400 // to set LLVM IR attribute.
402 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm." + name, llvmResTy,
403 adaptor.getOperands());
404 return mlir::success();
405}
406
407/// BoolAttr visitor.
408mlir::Value CIRAttrToValue::visitCirAttr(cir::BoolAttr boolAttr) {
409 mlir::Location loc = parentOp->getLoc();
410 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
411 mlir::Value boolVal = mlir::LLVM::ConstantOp::create(
412 rewriter, loc, converter->convertType(boolAttr.getType()),
413 boolAttr.getValue());
414 return emitToMemory(rewriter, layout, boolAttr.getType(), boolVal);
415}
416
417/// IntAttr visitor.
418mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
419 mlir::Location loc = parentOp->getLoc();
420 return mlir::LLVM::ConstantOp::create(
421 rewriter, loc, converter->convertType(intAttr.getType()),
422 intAttr.getValue());
423}
424
425/// FPAttr visitor.
426mlir::Value CIRAttrToValue::visitCirAttr(cir::FPAttr fltAttr) {
427 mlir::Location loc = parentOp->getLoc();
428 return mlir::LLVM::ConstantOp::create(
429 rewriter, loc, converter->convertType(fltAttr.getType()),
430 fltAttr.getValue());
431}
432
433/// ConstComplexAttr visitor.
434mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstComplexAttr complexAttr) {
435 auto complexType = mlir::cast<cir::ComplexType>(complexAttr.getType());
436 mlir::Type complexElemTy = complexType.getElementType();
437 mlir::Type complexElemLLVMTy = converter->convertType(complexElemTy);
438
439 mlir::Attribute components[2];
440 if (const auto intType = mlir::dyn_cast<cir::IntType>(complexElemTy)) {
441 components[0] = rewriter.getIntegerAttr(
442 complexElemLLVMTy,
443 mlir::cast<cir::IntAttr>(complexAttr.getReal()).getValue());
444 components[1] = rewriter.getIntegerAttr(
445 complexElemLLVMTy,
446 mlir::cast<cir::IntAttr>(complexAttr.getImag()).getValue());
447 } else {
448 components[0] = rewriter.getFloatAttr(
449 complexElemLLVMTy,
450 mlir::cast<cir::FPAttr>(complexAttr.getReal()).getValue());
451 components[1] = rewriter.getFloatAttr(
452 complexElemLLVMTy,
453 mlir::cast<cir::FPAttr>(complexAttr.getImag()).getValue());
454 }
455
456 mlir::Location loc = parentOp->getLoc();
457 return mlir::LLVM::ConstantOp::create(
458 rewriter, loc, converter->convertType(complexAttr.getType()),
459 rewriter.getArrayAttr(components));
460}
461
462/// ConstPtrAttr visitor.
463mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstPtrAttr ptrAttr) {
464 mlir::Location loc = parentOp->getLoc();
465 if (ptrAttr.isNullValue()) {
466 return mlir::LLVM::ZeroOp::create(
467 rewriter, loc, converter->convertType(ptrAttr.getType()));
468 }
469 mlir::DataLayout layout(parentOp->getParentOfType<mlir::ModuleOp>());
470 mlir::Value ptrVal = mlir::LLVM::ConstantOp::create(
471 rewriter, loc,
472 rewriter.getIntegerType(layout.getTypeSizeInBits(ptrAttr.getType())),
473 ptrAttr.getValue().getInt());
474 return mlir::LLVM::IntToPtrOp::create(
475 rewriter, loc, converter->convertType(ptrAttr.getType()), ptrVal);
476}
477
478// ConstArrayAttr visitor
479mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
480 mlir::Type llvmTy = converter->convertType(attr.getType());
481 mlir::Location loc = parentOp->getLoc();
482 mlir::Value result;
483
484 if (attr.hasTrailingZeros()) {
485 mlir::Type arrayTy = attr.getType();
486 result = mlir::LLVM::ZeroOp::create(rewriter, loc,
487 converter->convertType(arrayTy));
488 } else {
489 result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
490 }
491
492 // Iteratively lower each constant element of the array.
493 if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) {
494 for (auto [idx, elt] : llvm::enumerate(arrayAttr)) {
495 mlir::Value init = visit(elt);
496 result =
497 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
498 }
499 } else if (auto strAttr = mlir::dyn_cast<mlir::StringAttr>(attr.getElts())) {
500 // TODO(cir): this diverges from traditional lowering. Normally the string
501 // would be a global constant that is memcopied.
502 auto arrayTy = mlir::dyn_cast<cir::ArrayType>(strAttr.getType());
503 assert(arrayTy && "String attribute must have an array type");
504 mlir::Type eltTy = arrayTy.getElementType();
505 for (auto [idx, elt] : llvm::enumerate(strAttr)) {
506 auto init = mlir::LLVM::ConstantOp::create(
507 rewriter, loc, converter->convertType(eltTy), elt);
508 result =
509 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
510 }
511 } else {
512 llvm_unreachable("unexpected ConstArrayAttr elements");
513 }
514
515 return result;
516}
517
518/// ConstRecord visitor.
519mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstRecordAttr constRecord) {
520 const mlir::Type llvmTy = converter->convertType(constRecord.getType());
521 const mlir::Location loc = parentOp->getLoc();
522 mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
523
524 // Iteratively lower each constant element of the record.
525 for (auto [idx, elt] : llvm::enumerate(constRecord.getMembers())) {
526 mlir::Value init = visit(elt);
527 result =
528 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
529 }
530
531 return result;
532}
533
534/// ConstVectorAttr visitor.
535mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstVectorAttr attr) {
536 const mlir::Type llvmTy = converter->convertType(attr.getType());
537 const mlir::Location loc = parentOp->getLoc();
538
539 SmallVector<mlir::Attribute> mlirValues;
540 for (const mlir::Attribute elementAttr : attr.getElts()) {
541 mlir::Attribute mlirAttr;
542 if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(elementAttr)) {
543 mlirAttr = rewriter.getIntegerAttr(
544 converter->convertType(intAttr.getType()), intAttr.getValue());
545 } else if (auto floatAttr = mlir::dyn_cast<cir::FPAttr>(elementAttr)) {
546 mlirAttr = rewriter.getFloatAttr(
547 converter->convertType(floatAttr.getType()), floatAttr.getValue());
548 } else {
549 llvm_unreachable(
550 "vector constant with an element that is neither an int nor a float");
551 }
552 mlirValues.push_back(mlirAttr);
553 }
554
555 return mlir::LLVM::ConstantOp::create(
556 rewriter, loc, llvmTy,
557 mlir::DenseElementsAttr::get(mlir::cast<mlir::ShapedType>(llvmTy),
558 mlirValues));
559}
560
561// GlobalViewAttr visitor.
562mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
563 auto moduleOp = parentOp->getParentOfType<mlir::ModuleOp>();
564 mlir::DataLayout dataLayout(moduleOp);
565 mlir::Type sourceType;
566 unsigned sourceAddrSpace = 0;
567 llvm::StringRef symName;
568 mlir::Operation *sourceSymbol =
569 mlir::SymbolTable::lookupSymbolIn(moduleOp, globalAttr.getSymbol());
570 if (auto llvmSymbol = dyn_cast<mlir::LLVM::GlobalOp>(sourceSymbol)) {
571 sourceType = llvmSymbol.getType();
572 symName = llvmSymbol.getSymName();
573 sourceAddrSpace = llvmSymbol.getAddrSpace();
574 } else if (auto cirSymbol = dyn_cast<cir::GlobalOp>(sourceSymbol)) {
575 sourceType =
576 convertTypeForMemory(*converter, dataLayout, cirSymbol.getSymType());
577 symName = cirSymbol.getSymName();
578 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
579 cirSymbol.getAddrSpaceAttr()))
580 sourceAddrSpace = targetAS.getValue();
581 } else if (auto llvmFun = dyn_cast<mlir::LLVM::LLVMFuncOp>(sourceSymbol)) {
582 sourceType = llvmFun.getFunctionType();
583 symName = llvmFun.getSymName();
584 } else if (auto fun = dyn_cast<cir::FuncOp>(sourceSymbol)) {
585 sourceType = converter->convertType(fun.getFunctionType());
586 symName = fun.getSymName();
587 } else if (auto alias = dyn_cast<mlir::LLVM::AliasOp>(sourceSymbol)) {
588 sourceType = alias.getType();
589 symName = alias.getSymName();
590 } else {
591 llvm_unreachable("Unexpected GlobalOp type");
592 }
593
594 mlir::Location loc = parentOp->getLoc();
595 mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
596 rewriter, loc,
597 mlir::LLVM::LLVMPointerType::get(rewriter.getContext(), sourceAddrSpace),
598 symName);
599
600 if (globalAttr.getIndices()) {
601 llvm::SmallVector<mlir::LLVM::GEPArg> indices;
602
603 if (mlir::isa<mlir::LLVM::LLVMArrayType, mlir::LLVM::LLVMStructType>(
604 sourceType))
605 indices.push_back(0);
606
607 for (mlir::Attribute idx : globalAttr.getIndices()) {
608 auto intAttr = mlir::cast<mlir::IntegerAttr>(idx);
609 indices.push_back(intAttr.getValue().getSExtValue());
610 }
611 mlir::Type resTy = addrOp.getType();
612 mlir::Type eltTy = converter->convertType(sourceType);
613 addrOp =
614 mlir::LLVM::GEPOp::create(rewriter, loc, resTy, eltTy, addrOp, indices,
615 mlir::LLVM::GEPNoWrapFlags::none);
616 }
617
618 // We can have a global view with an integer type in the case of method
619 // pointers. With the Itanium ABI, the #cir.method attribute is lowered to a
620 // #cir.global_view with a pointer-sized integer representing the address of
621 // the method.
622 if (auto intTy = mlir::dyn_cast<cir::IntType>(globalAttr.getType())) {
623 mlir::Type llvmDstTy = converter->convertType(globalAttr.getType());
624 return mlir::LLVM::PtrToIntOp::create(rewriter, parentOp->getLoc(),
625 llvmDstTy, addrOp);
626 }
627
628 if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(globalAttr.getType())) {
629 auto llvmDstTy = converter->convertType<mlir::LLVM::LLVMPointerType>(ptrTy);
630 unsigned dstAddrSpace = llvmDstTy.getAddressSpace();
631
632 if (sourceAddrSpace != dstAddrSpace)
633 addrOp = mlir::LLVM::AddrSpaceCastOp::create(rewriter, parentOp->getLoc(),
634 llvmDstTy, addrOp);
635
636 mlir::Type llvmEltTy =
637 convertTypeForMemory(*converter, dataLayout, ptrTy.getPointee());
638
639 // No further cast needed if the pointee type already matches.
640 if (llvmEltTy == sourceType)
641 return addrOp;
642
643 // With opaque pointers, the pointer type is already correct (either from
644 // the original AddressOfOp or after an addrspacecast) — skip the
645 // redundant bitcast.
646 if (addrOp.getType() == llvmDstTy)
647 return addrOp;
648
649 return mlir::LLVM::BitcastOp::create(rewriter, parentOp->getLoc(),
650 llvmDstTy, addrOp);
651 }
652
653 if (mlir::isa<cir::VPtrType>(globalAttr.getType()))
654 return addrOp;
655
656 llvm_unreachable("Expecting pointer or integer type for GlobalViewAttr");
657}
658
659// TypeInfoAttr visitor.
660mlir::Value CIRAttrToValue::visitCirAttr(cir::TypeInfoAttr typeInfoAttr) {
661 mlir::Type llvmTy = converter->convertType(typeInfoAttr.getType());
662 mlir::Location loc = parentOp->getLoc();
663 mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
664
665 for (auto [idx, elt] : llvm::enumerate(typeInfoAttr.getData())) {
666 mlir::Value init = visit(elt);
667 result =
668 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
669 }
670
671 return result;
672}
673
674/// UndefAttr visitor.
675mlir::Value CIRAttrToValue::visitCirAttr(cir::UndefAttr undefAttr) {
676 mlir::Location loc = parentOp->getLoc();
677 return mlir::LLVM::UndefOp::create(
678 rewriter, loc, converter->convertType(undefAttr.getType()));
679}
680
681/// PoisonAttr visitor.
682mlir::Value CIRAttrToValue::visitCirAttr(cir::PoisonAttr poisonAttr) {
683 mlir::Location loc = parentOp->getLoc();
684 return mlir::LLVM::PoisonOp::create(
685 rewriter, loc, converter->convertType(poisonAttr.getType()));
686}
687
688// VTableAttr visitor.
689mlir::Value CIRAttrToValue::visitCirAttr(cir::VTableAttr vtableArr) {
690 mlir::Type llvmTy = converter->convertType(vtableArr.getType());
691 mlir::Location loc = parentOp->getLoc();
692 mlir::Value result = mlir::LLVM::UndefOp::create(rewriter, loc, llvmTy);
693
694 for (auto [idx, elt] : llvm::enumerate(vtableArr.getData())) {
695 mlir::Value init = visit(elt);
696 result =
697 mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
698 }
699
700 return result;
701}
702
703/// ZeroAttr visitor.
704mlir::Value CIRAttrToValue::visitCirAttr(cir::ZeroAttr attr) {
705 mlir::Location loc = parentOp->getLoc();
706 return mlir::LLVM::ZeroOp::create(rewriter, loc,
707 converter->convertType(attr.getType()));
708}
709
710// This class handles rewriting initializer attributes for types that do not
711// require region initialization.
713public:
714 GlobalInitAttrRewriter(mlir::Type type,
715 mlir::ConversionPatternRewriter &rewriter)
716 : llvmType(type), rewriter(rewriter) {}
717
718 mlir::Attribute visit(mlir::Attribute attr) {
719 return llvm::TypeSwitch<mlir::Attribute, mlir::Attribute>(attr)
720 .Case<cir::IntAttr, cir::FPAttr, cir::BoolAttr>(
721 [&](auto attrT) { return visitCirAttr(attrT); })
722 .Default([&](auto attrT) { return mlir::Attribute(); });
723 }
724
725 mlir::Attribute visitCirAttr(cir::IntAttr attr) {
726 return rewriter.getIntegerAttr(llvmType, attr.getValue());
727 }
728
729 mlir::Attribute visitCirAttr(cir::FPAttr attr) {
730 return rewriter.getFloatAttr(llvmType, attr.getValue());
731 }
732
733 mlir::Attribute visitCirAttr(cir::BoolAttr attr) {
734 return rewriter.getBoolAttr(attr.getValue());
735 }
736
737private:
738 mlir::Type llvmType;
739 mlir::ConversionPatternRewriter &rewriter;
740};
741
742// This pass requires the CIR to be in a "flat" state. All blocks in each
743// function must belong to the parent region. Once scopes and control flow
744// are implemented in CIR, a pass will be run before this one to flatten
745// the CIR and get it into the state that this pass requires.
747 : public mlir::PassWrapper<ConvertCIRToLLVMPass,
748 mlir::OperationPass<mlir::ModuleOp>> {
749 void getDependentDialects(mlir::DialectRegistry &registry) const override {
750 registry.insert<mlir::BuiltinDialect, mlir::DLTIDialect,
751 mlir::LLVM::LLVMDialect, mlir::func::FuncDialect>();
752 }
753 void runOnOperation() final;
754
755 void processCIRAttrs(mlir::ModuleOp module);
756
757 void resolveBlockAddressOp(LLVMBlockAddressInfo &blockInfoAddr);
758
759 /// Collect (symbol_name, annotations, loc) from cir.func and cir.global ops
760 /// before the conversion runs (the annotations attribute is dropped during
761 /// FuncOp/GlobalOp lowering).
762 void collectGlobalAnnotations(mlir::ModuleOp module);
763
764 /// Emit @llvm.global.annotations and supporting string/args constants from
765 /// the previously-collected annotations. Mirrors what OGCG produces.
766 void buildGlobalAnnotationsVar(mlir::ModuleOp module);
767
768 StringRef getDescription() const override {
769 return "Convert the prepared CIR dialect module to LLVM dialect";
770 }
771
772 StringRef getArgument() const override { return "cir-flat-to-llvm"; }
773
774private:
775 /// One annotation entry collected pre-conversion.
776 struct CollectedAnnotation {
777 mlir::StringAttr symName;
778 cir::AnnotationAttr annotation;
779 mlir::Location loc;
780 CollectedAnnotation(mlir::StringAttr symName,
781 cir::AnnotationAttr annotation, mlir::Location loc)
782 : symName(symName), annotation(annotation), loc(loc) {}
783 };
784 llvm::SmallVector<CollectedAnnotation> collectedAnnotations;
785};
786
787mlir::LogicalResult CIRToLLVMIsFPClassOpLowering::matchAndRewrite(
788 cir::IsFPClassOp op, OpAdaptor adaptor,
789 mlir::ConversionPatternRewriter &rewriter) const {
790 mlir::Value src = adaptor.getSrc();
791 cir::FPClassTest flags = adaptor.getFlags();
792 mlir::IntegerType retTy = rewriter.getI1Type();
793
794 rewriter.replaceOpWithNewOp<mlir::LLVM::IsFPClass>(
795 op, retTy, src, static_cast<uint32_t>(flags));
796 return mlir::success();
797}
798
799mlir::LogicalResult CIRToLLVMSignBitOpLowering::matchAndRewrite(
800 cir::SignBitOp op, OpAdaptor adaptor,
801 mlir::ConversionPatternRewriter &rewriter) const {
803
804 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
805 int width = layout.getTypeSizeInBits(op.getInput().getType());
806 if (auto longDoubleType =
807 mlir::dyn_cast<cir::LongDoubleType>(op.getInput().getType())) {
808 if (mlir::isa<cir::FP80Type>(longDoubleType.getUnderlying())) {
809 // If the underlying type of LongDouble is FP80Type,
810 // DataLayout::getTypeSizeInBits returns 128.
811 // See https://github.com/llvm/clangir/issues/1057.
812 // Set the width to 80 manually.
813 width = 80;
814 }
815 }
816 mlir::Type intTy = mlir::IntegerType::get(rewriter.getContext(), width);
817 auto bitcast = mlir::LLVM::BitcastOp::create(rewriter, op->getLoc(), intTy,
818 adaptor.getInput());
819
820 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op->getLoc(), intTy, 0);
821 auto cmpResult = mlir::LLVM::ICmpOp::create(rewriter, op.getLoc(),
822 mlir::LLVM::ICmpPredicate::slt,
823 bitcast.getResult(), zero);
824 rewriter.replaceOp(op, cmpResult);
825 return mlir::success();
826}
827
828mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite(
829 cir::AssumeOp op, OpAdaptor adaptor,
830 mlir::ConversionPatternRewriter &rewriter) const {
831 mlir::Value cond = adaptor.getPredicate();
832 if (op.getBundleKind() == cir::AssumeBundleKind::None) {
833 rewriter.replaceOpWithNewOp<mlir::LLVM::AssumeOp>(op, cond);
834 return mlir::success();
835 }
836
837 llvm::StringRef tag = cir::stringifyAssumeBundleKind(op.getBundleKind());
838 rewriter.replaceOpWithNewOp<mlir::LLVM::AssumeOp>(op, cond, tag,
839 adaptor.getBundleArgs());
840 return mlir::success();
841}
842
843static mlir::LLVM::AtomicOrdering
844getLLVMMemOrder(std::optional<cir::MemOrder> memorder) {
845 if (!memorder)
846 return mlir::LLVM::AtomicOrdering::not_atomic;
847 switch (*memorder) {
848 case cir::MemOrder::Relaxed:
849 return mlir::LLVM::AtomicOrdering::monotonic;
850 case cir::MemOrder::Consume:
851 case cir::MemOrder::Acquire:
852 return mlir::LLVM::AtomicOrdering::acquire;
853 case cir::MemOrder::Release:
854 return mlir::LLVM::AtomicOrdering::release;
855 case cir::MemOrder::AcquireRelease:
856 return mlir::LLVM::AtomicOrdering::acq_rel;
857 case cir::MemOrder::SequentiallyConsistent:
858 return mlir::LLVM::AtomicOrdering::seq_cst;
859 }
860 llvm_unreachable("unknown memory order");
861}
862
863static llvm::StringRef getLLVMSyncScope(cir::SyncScopeKind syncScope) {
864 return syncScope == cir::SyncScopeKind::SingleThread ? "singlethread" : "";
865}
866
867static std::optional<llvm::StringRef>
868getLLVMSyncScope(std::optional<cir::SyncScopeKind> syncScope) {
869 if (syncScope.has_value())
870 return getLLVMSyncScope(*syncScope);
871 return std::nullopt;
872}
873
874mlir::LogicalResult CIRToLLVMAtomicCmpXchgOpLowering::matchAndRewrite(
875 cir::AtomicCmpXchgOp op, OpAdaptor adaptor,
876 mlir::ConversionPatternRewriter &rewriter) const {
877 mlir::Value expected = adaptor.getExpected();
878 mlir::Value desired = adaptor.getDesired();
879
880 auto cmpxchg = mlir::LLVM::AtomicCmpXchgOp::create(
881 rewriter, op.getLoc(), adaptor.getPtr(), expected, desired,
882 getLLVMMemOrder(adaptor.getSuccOrder()),
883 getLLVMMemOrder(adaptor.getFailOrder()),
884 getLLVMSyncScope(op.getSyncScope()));
885
886 cmpxchg.setAlignment(adaptor.getAlignment());
887 cmpxchg.setWeak(adaptor.getWeak());
888 cmpxchg.setVolatile_(adaptor.getIsVolatile());
889
890 // Check result and apply stores accordingly.
891 auto old = mlir::LLVM::ExtractValueOp::create(rewriter, op.getLoc(),
892 cmpxchg.getResult(), 0);
893 auto cmp = mlir::LLVM::ExtractValueOp::create(rewriter, op.getLoc(),
894 cmpxchg.getResult(), 1);
895
896 rewriter.replaceOp(op, {old, cmp});
897 return mlir::success();
898}
899
900mlir::LogicalResult CIRToLLVMAtomicXchgOpLowering::matchAndRewrite(
901 cir::AtomicXchgOp op, OpAdaptor adaptor,
902 mlir::ConversionPatternRewriter &rewriter) const {
904 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(adaptor.getMemOrder());
905 llvm::StringRef llvmSyncScope = getLLVMSyncScope(adaptor.getSyncScope());
906 rewriter.replaceOpWithNewOp<mlir::LLVM::AtomicRMWOp>(
907 op, mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(), adaptor.getVal(),
908 llvmOrder, llvmSyncScope);
909 return mlir::success();
910}
911
912mlir::LogicalResult CIRToLLVMAtomicTestAndSetOpLowering::matchAndRewrite(
913 cir::AtomicTestAndSetOp op, OpAdaptor adaptor,
914 mlir::ConversionPatternRewriter &rewriter) const {
916
917 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
918
919 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
920 rewriter.getI8Type(), 1);
921 auto rmw = mlir::LLVM::AtomicRMWOp::create(
922 rewriter, op.getLoc(), mlir::LLVM::AtomicBinOp::xchg, adaptor.getPtr(),
923 one, llvmOrder, /*syncscope=*/llvm::StringRef(),
924 adaptor.getAlignment().value_or(0), op.getIsVolatile());
925
926 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
927 rewriter.getI8Type(), 0);
928 auto cmp = mlir::LLVM::ICmpOp::create(
929 rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, rmw, zero);
930
931 rewriter.replaceOp(op, cmp);
932 return mlir::success();
933}
934
935mlir::LogicalResult CIRToLLVMAtomicClearOpLowering::matchAndRewrite(
936 cir::AtomicClearOp op, OpAdaptor adaptor,
937 mlir::ConversionPatternRewriter &rewriter) const {
939
940 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
941 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
942 rewriter.getI8Type(), 0);
943 auto store = mlir::LLVM::StoreOp::create(
944 rewriter, op.getLoc(), zero, adaptor.getPtr(),
945 adaptor.getAlignment().value_or(0), op.getIsVolatile(),
946 /*isNonTemporal=*/false, /*isInvariantGroup=*/false, llvmOrder);
947
948 rewriter.replaceOp(op, store);
949 return mlir::success();
950}
951
952mlir::LogicalResult CIRToLLVMAtomicFenceOpLowering::matchAndRewrite(
953 cir::AtomicFenceOp op, OpAdaptor adaptor,
954 mlir::ConversionPatternRewriter &rewriter) const {
955 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(adaptor.getOrdering());
956
957 auto fence = mlir::LLVM::FenceOp::create(rewriter, op.getLoc(), llvmOrder);
958 fence.setSyncscope(getLLVMSyncScope(adaptor.getSyncscope()));
959
960 rewriter.replaceOp(op, fence);
961
962 return mlir::success();
963}
964
965static mlir::LLVM::AtomicBinOp
966getLLVMAtomicBinOp(cir::AtomicFetchKind k, bool isInt, bool isSignedInt) {
967 switch (k) {
968 case cir::AtomicFetchKind::Add:
969 return isInt ? mlir::LLVM::AtomicBinOp::add : mlir::LLVM::AtomicBinOp::fadd;
970 case cir::AtomicFetchKind::Sub:
971 return isInt ? mlir::LLVM::AtomicBinOp::sub : mlir::LLVM::AtomicBinOp::fsub;
972 case cir::AtomicFetchKind::And:
973 return mlir::LLVM::AtomicBinOp::_and;
974 case cir::AtomicFetchKind::Xor:
975 return mlir::LLVM::AtomicBinOp::_xor;
976 case cir::AtomicFetchKind::Or:
977 return mlir::LLVM::AtomicBinOp::_or;
978 case cir::AtomicFetchKind::Nand:
979 return mlir::LLVM::AtomicBinOp::nand;
980 case cir::AtomicFetchKind::Max: {
981 if (!isInt)
982 return mlir::LLVM::AtomicBinOp::fmax;
983 return isSignedInt ? mlir::LLVM::AtomicBinOp::max
984 : mlir::LLVM::AtomicBinOp::umax;
985 }
986 case cir::AtomicFetchKind::Min: {
987 if (!isInt)
988 return mlir::LLVM::AtomicBinOp::fmin;
989 return isSignedInt ? mlir::LLVM::AtomicBinOp::min
990 : mlir::LLVM::AtomicBinOp::umin;
991 }
992 case cir::AtomicFetchKind::UIncWrap:
993 return mlir::LLVM::AtomicBinOp::uinc_wrap;
994 case cir::AtomicFetchKind::UDecWrap:
995 return mlir::LLVM::AtomicBinOp::udec_wrap;
996 }
997 llvm_unreachable("Unknown atomic fetch opcode");
998}
999
1000static llvm::StringLiteral getLLVMBinopForPostAtomic(cir::AtomicFetchKind k,
1001 bool isInt) {
1002 switch (k) {
1003 case cir::AtomicFetchKind::Add:
1004 return isInt ? mlir::LLVM::AddOp::getOperationName()
1005 : mlir::LLVM::FAddOp::getOperationName();
1006 case cir::AtomicFetchKind::Sub:
1007 return isInt ? mlir::LLVM::SubOp::getOperationName()
1008 : mlir::LLVM::FSubOp::getOperationName();
1009 case cir::AtomicFetchKind::And:
1010 return mlir::LLVM::AndOp::getOperationName();
1011 case cir::AtomicFetchKind::Xor:
1012 return mlir::LLVM::XOrOp::getOperationName();
1013 case cir::AtomicFetchKind::Or:
1014 return mlir::LLVM::OrOp::getOperationName();
1015 case cir::AtomicFetchKind::Nand:
1016 // There's no nand binop in LLVM, this is later fixed with a not.
1017 return mlir::LLVM::AndOp::getOperationName();
1018 case cir::AtomicFetchKind::Max:
1019 case cir::AtomicFetchKind::Min:
1020 llvm_unreachable("handled in buildMinMaxPostOp");
1021 case cir::AtomicFetchKind::UIncWrap:
1022 case cir::AtomicFetchKind::UDecWrap:
1023 llvm_unreachable("uinc_wrap and udec_wrap are always fetch_first");
1024 }
1025 llvm_unreachable("Unknown atomic fetch opcode");
1026}
1027
1028mlir::Value CIRToLLVMAtomicFetchOpLowering::buildPostOp(
1029 cir::AtomicFetchOp op, OpAdaptor adaptor,
1030 mlir::ConversionPatternRewriter &rewriter, mlir::Value rmwVal,
1031 bool isInt) const {
1032 SmallVector<mlir::Value> atomicOperands = {rmwVal, adaptor.getVal()};
1033 SmallVector<mlir::Type> atomicResTys = {rmwVal.getType()};
1034 return rewriter
1035 .create(op.getLoc(),
1036 rewriter.getStringAttr(
1037 getLLVMBinopForPostAtomic(op.getBinop(), isInt)),
1038 atomicOperands, atomicResTys, {})
1039 ->getResult(0);
1040}
1041
1042mlir::Value CIRToLLVMAtomicFetchOpLowering::buildMinMaxPostOp(
1043 cir::AtomicFetchOp op, OpAdaptor adaptor,
1044 mlir::ConversionPatternRewriter &rewriter, mlir::Value rmwVal, bool isInt,
1045 bool isSigned) const {
1046 mlir::Location loc = op.getLoc();
1047
1048 if (!isInt) {
1049 if (op.getBinop() == cir::AtomicFetchKind::Max)
1050 return mlir::LLVM::MaxNumOp::create(rewriter, loc, rmwVal,
1051 adaptor.getVal());
1052 return mlir::LLVM::MinNumOp::create(rewriter, loc, rmwVal,
1053 adaptor.getVal());
1054 }
1055
1056 mlir::LLVM::ICmpPredicate pred;
1057 if (op.getBinop() == cir::AtomicFetchKind::Max) {
1058 pred = isSigned ? mlir::LLVM::ICmpPredicate::sgt
1059 : mlir::LLVM::ICmpPredicate::ugt;
1060 } else { // Min
1061 pred = isSigned ? mlir::LLVM::ICmpPredicate::slt
1062 : mlir::LLVM::ICmpPredicate::ult;
1063 }
1064 mlir::Value cmp = mlir::LLVM::ICmpOp::create(
1065 rewriter, loc,
1066 mlir::LLVM::ICmpPredicateAttr::get(rewriter.getContext(), pred), rmwVal,
1067 adaptor.getVal());
1068 return mlir::LLVM::SelectOp::create(rewriter, loc, cmp, rmwVal,
1069 adaptor.getVal());
1070}
1071
1072mlir::LogicalResult CIRToLLVMAtomicFetchOpLowering::matchAndRewrite(
1073 cir::AtomicFetchOp op, OpAdaptor adaptor,
1074 mlir::ConversionPatternRewriter &rewriter) const {
1075 bool isInt = false;
1076 bool isSignedInt = false;
1077 if (auto intTy = mlir::dyn_cast<cir::IntType>(op.getVal().getType())) {
1078 isInt = true;
1079 isSignedInt = intTy.isSigned();
1080 } else if (mlir::isa<cir::SingleType, cir::DoubleType>(
1081 op.getVal().getType())) {
1082 isInt = false;
1083 } else {
1084 return op.emitError() << "Unsupported type: " << op.getVal().getType();
1085 }
1086
1087 mlir::LLVM::AtomicOrdering llvmOrder = getLLVMMemOrder(op.getMemOrder());
1088 llvm::StringRef llvmSyncScope = getLLVMSyncScope(op.getSyncScope());
1089 mlir::LLVM::AtomicBinOp llvmBinOp =
1090 getLLVMAtomicBinOp(op.getBinop(), isInt, isSignedInt);
1091 auto rmwVal = mlir::LLVM::AtomicRMWOp::create(
1092 rewriter, op.getLoc(), llvmBinOp, adaptor.getPtr(), adaptor.getVal(),
1093 llvmOrder, llvmSyncScope);
1094
1095 mlir::Value result = rmwVal.getResult();
1096 if (!op.getFetchFirst()) {
1097 if (op.getBinop() == cir::AtomicFetchKind::Max ||
1098 op.getBinop() == cir::AtomicFetchKind::Min)
1099 result = buildMinMaxPostOp(op, adaptor, rewriter, rmwVal.getRes(), isInt,
1100 isSignedInt);
1101 else
1102 result = buildPostOp(op, adaptor, rewriter, rmwVal.getRes(), isInt);
1103
1104 // Compensate lack of nand binop in LLVM IR.
1105 if (op.getBinop() == cir::AtomicFetchKind::Nand) {
1106 auto negOne = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
1107 result.getType(), -1);
1108 result = mlir::LLVM::XOrOp::create(rewriter, op.getLoc(), result, negOne);
1109 }
1110 }
1111
1112 rewriter.replaceOp(op, result);
1113 return mlir::success();
1114}
1115
1116mlir::LogicalResult CIRToLLVMBitClrsbOpLowering::matchAndRewrite(
1117 cir::BitClrsbOp op, OpAdaptor adaptor,
1118 mlir::ConversionPatternRewriter &rewriter) const {
1119 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
1120 adaptor.getInput().getType(), 0);
1121 auto isNeg = mlir::LLVM::ICmpOp::create(
1122 rewriter, op.getLoc(),
1123 mlir::LLVM::ICmpPredicateAttr::get(rewriter.getContext(),
1124 mlir::LLVM::ICmpPredicate::slt),
1125 adaptor.getInput(), zero);
1126
1127 auto negOne = mlir::LLVM::ConstantOp::create(
1128 rewriter, op.getLoc(), adaptor.getInput().getType(), -1);
1129 auto flipped = mlir::LLVM::XOrOp::create(rewriter, op.getLoc(),
1130 adaptor.getInput(), negOne);
1131
1132 auto select = mlir::LLVM::SelectOp::create(rewriter, op.getLoc(), isNeg,
1133 flipped, adaptor.getInput());
1134
1135 auto resTy = getTypeConverter()->convertType(op.getType());
1136 auto clz = mlir::LLVM::CountLeadingZerosOp::create(
1137 rewriter, op.getLoc(), resTy, select, /*is_zero_poison=*/false);
1138
1139 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 1);
1140 auto res = mlir::LLVM::SubOp::create(rewriter, op.getLoc(), clz, one,
1141 mlir::LLVM::IntegerOverflowFlags::nuw);
1142 rewriter.replaceOp(op, res);
1143
1144 return mlir::LogicalResult::success();
1145}
1146
1147mlir::LogicalResult CIRToLLVMBitClzOpLowering::matchAndRewrite(
1148 cir::BitClzOp op, OpAdaptor adaptor,
1149 mlir::ConversionPatternRewriter &rewriter) const {
1150 auto resTy = getTypeConverter()->convertType(op.getType());
1151 auto llvmOp = mlir::LLVM::CountLeadingZerosOp::create(
1152 rewriter, op.getLoc(), resTy, adaptor.getInput(), op.getPoisonZero());
1153 rewriter.replaceOp(op, llvmOp);
1154 return mlir::LogicalResult::success();
1155}
1156
1157mlir::LogicalResult CIRToLLVMBitCtzOpLowering::matchAndRewrite(
1158 cir::BitCtzOp op, OpAdaptor adaptor,
1159 mlir::ConversionPatternRewriter &rewriter) const {
1160 auto resTy = getTypeConverter()->convertType(op.getType());
1161 auto llvmOp = mlir::LLVM::CountTrailingZerosOp::create(
1162 rewriter, op.getLoc(), resTy, adaptor.getInput(), op.getPoisonZero());
1163 rewriter.replaceOp(op, llvmOp);
1164 return mlir::LogicalResult::success();
1165}
1166
1167mlir::LogicalResult CIRToLLVMBitFfsOpLowering::matchAndRewrite(
1168 cir::BitFfsOp op, OpAdaptor adaptor,
1169 mlir::ConversionPatternRewriter &rewriter) const {
1170 auto resTy = getTypeConverter()->convertType(op.getType());
1171 auto ctz = mlir::LLVM::CountTrailingZerosOp::create(rewriter, op.getLoc(),
1172 resTy, adaptor.getInput(),
1173 /*is_zero_poison=*/true);
1174
1175 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 1);
1176 auto ctzAddOne = mlir::LLVM::AddOp::create(rewriter, op.getLoc(), ctz, one);
1177
1178 auto zeroInputTy = mlir::LLVM::ConstantOp::create(
1179 rewriter, op.getLoc(), adaptor.getInput().getType(), 0);
1180 auto isZero = mlir::LLVM::ICmpOp::create(
1181 rewriter, op.getLoc(),
1182 mlir::LLVM::ICmpPredicateAttr::get(rewriter.getContext(),
1183 mlir::LLVM::ICmpPredicate::eq),
1184 adaptor.getInput(), zeroInputTy);
1185
1186 auto zero = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 0);
1187 auto res = mlir::LLVM::SelectOp::create(rewriter, op.getLoc(), isZero, zero,
1188 ctzAddOne);
1189 rewriter.replaceOp(op, res);
1190
1191 return mlir::LogicalResult::success();
1192}
1193
1194mlir::LogicalResult CIRToLLVMBitParityOpLowering::matchAndRewrite(
1195 cir::BitParityOp op, OpAdaptor adaptor,
1196 mlir::ConversionPatternRewriter &rewriter) const {
1197 auto resTy = getTypeConverter()->convertType(op.getType());
1198 auto popcnt = mlir::LLVM::CtPopOp::create(rewriter, op.getLoc(), resTy,
1199 adaptor.getInput());
1200
1201 auto one = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(), resTy, 1);
1202 auto popcntMod2 =
1203 mlir::LLVM::AndOp::create(rewriter, op.getLoc(), popcnt, one);
1204 rewriter.replaceOp(op, popcntMod2);
1205
1206 return mlir::LogicalResult::success();
1207}
1208
1209mlir::LogicalResult CIRToLLVMBitPopcountOpLowering::matchAndRewrite(
1210 cir::BitPopcountOp op, OpAdaptor adaptor,
1211 mlir::ConversionPatternRewriter &rewriter) const {
1212 auto resTy = getTypeConverter()->convertType(op.getType());
1213 auto llvmOp = mlir::LLVM::CtPopOp::create(rewriter, op.getLoc(), resTy,
1214 adaptor.getInput());
1215 rewriter.replaceOp(op, llvmOp);
1216 return mlir::LogicalResult::success();
1217}
1218
1219mlir::LogicalResult CIRToLLVMBrCondOpLowering::matchAndRewrite(
1220 cir::BrCondOp brOp, OpAdaptor adaptor,
1221 mlir::ConversionPatternRewriter &rewriter) const {
1222 // When ZExtOp is implemented, we'll need to check if the condition is a
1223 // ZExtOp and if so, delete it if it has a single use.
1225
1226 mlir::Value i1Condition = adaptor.getCond();
1227
1228 rewriter.replaceOpWithNewOp<mlir::LLVM::CondBrOp>(
1229 brOp, i1Condition, brOp.getDestTrue(), adaptor.getDestOperandsTrue(),
1230 brOp.getDestFalse(), adaptor.getDestOperandsFalse());
1231
1232 return mlir::success();
1233}
1234
1235mlir::Type CIRToLLVMCastOpLowering::convertTy(mlir::Type ty) const {
1236 return getTypeConverter()->convertType(ty);
1237}
1238
1239mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite(
1240 cir::CastOp castOp, OpAdaptor adaptor,
1241 mlir::ConversionPatternRewriter &rewriter) const {
1242 // For arithmetic conversions, LLVM IR uses the same instruction to convert
1243 // both individual scalars and entire vectors. This lowering pass handles
1244 // both situations.
1245
1246 switch (castOp.getKind()) {
1247 case cir::CastKind::array_to_ptrdecay: {
1248 const auto ptrTy = mlir::cast<cir::PointerType>(castOp.getType());
1249 mlir::Value sourceValue = adaptor.getSrc();
1250 mlir::Type targetType = convertTy(ptrTy);
1251 mlir::Type elementTy = convertTypeForMemory(*getTypeConverter(), dataLayout,
1252 ptrTy.getPointee());
1253 llvm::SmallVector<mlir::LLVM::GEPArg> offset{0};
1254 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1255 castOp, targetType, elementTy, sourceValue, offset);
1256 break;
1257 }
1258 case cir::CastKind::int_to_bool: {
1259 mlir::Value llvmSrcVal = adaptor.getSrc();
1260 // getZeroAttr yields a splat for vector source types so this also
1261 // handles element-wise int-to-bool conversions (e.g. an ext_vector
1262 // __builtin_convertvector to bool).
1263 mlir::Value zeroInt = mlir::LLVM::ConstantOp::create(
1264 rewriter, castOp.getLoc(), llvmSrcVal.getType(),
1265 rewriter.getZeroAttr(llvmSrcVal.getType()));
1266 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1267 castOp, mlir::LLVM::ICmpPredicate::ne, llvmSrcVal, zeroInt);
1268 break;
1269 }
1270 case cir::CastKind::integral: {
1271 mlir::Type srcType = castOp.getSrc().getType();
1272 mlir::Type dstType = castOp.getType();
1273 mlir::Value llvmSrcVal = adaptor.getSrc();
1274 mlir::Type llvmDstType = getTypeConverter()->convertType(dstType);
1275 cir::IntType srcIntType =
1276 mlir::cast<cir::IntType>(elementTypeIfVector(srcType));
1277 cir::IntType dstIntType =
1278 mlir::cast<cir::IntType>(elementTypeIfVector(dstType));
1279 rewriter.replaceOp(castOp, getLLVMIntCast(rewriter, llvmSrcVal, llvmDstType,
1280 srcIntType.isUnsigned(),
1281 srcIntType.getWidth(),
1282 dstIntType.getWidth()));
1283 break;
1284 }
1285 case cir::CastKind::floating: {
1286 mlir::Value llvmSrcVal = adaptor.getSrc();
1287 mlir::Type llvmDstTy = getTypeConverter()->convertType(castOp.getType());
1288
1289 mlir::Type srcTy = elementTypeIfVector(castOp.getSrc().getType());
1290 mlir::Type dstTy = elementTypeIfVector(castOp.getType());
1291
1292 if (!mlir::isa<cir::FPTypeInterface>(dstTy) ||
1293 !mlir::isa<cir::FPTypeInterface>(srcTy))
1294 return castOp.emitError() << "NYI cast from " << srcTy << " to " << dstTy;
1295
1296 auto getFloatWidth = [](mlir::Type ty) -> unsigned {
1297 return mlir::cast<cir::FPTypeInterface>(ty).getWidth();
1298 };
1299
1300 if (getFloatWidth(srcTy) > getFloatWidth(dstTy))
1301 rewriter.replaceOpWithNewOp<mlir::LLVM::FPTruncOp>(castOp, llvmDstTy,
1302 llvmSrcVal);
1303 else
1304 rewriter.replaceOpWithNewOp<mlir::LLVM::FPExtOp>(castOp, llvmDstTy,
1305 llvmSrcVal);
1306 return mlir::success();
1307 }
1308 case cir::CastKind::int_to_ptr: {
1309 auto dstTy = mlir::cast<cir::PointerType>(castOp.getType());
1310 mlir::Value llvmSrcVal = adaptor.getSrc();
1311 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1312 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(castOp, llvmDstTy,
1313 llvmSrcVal);
1314 return mlir::success();
1315 }
1316 case cir::CastKind::ptr_to_int: {
1317 auto dstTy = mlir::cast<cir::IntType>(castOp.getType());
1318 mlir::Value llvmSrcVal = adaptor.getSrc();
1319 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1320 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(castOp, llvmDstTy,
1321 llvmSrcVal);
1322 return mlir::success();
1323 }
1324 case cir::CastKind::float_to_bool: {
1325 mlir::Value llvmSrcVal = adaptor.getSrc();
1326 auto kind = mlir::LLVM::FCmpPredicate::une;
1327
1328 // Check if float is not equal to zero. getZeroAttr yields a splat
1329 // for vector source types so this also handles element-wise
1330 // float-to-bool conversions.
1331 auto zeroFloat = mlir::LLVM::ConstantOp::create(
1332 rewriter, castOp.getLoc(), llvmSrcVal.getType(),
1333 rewriter.getZeroAttr(llvmSrcVal.getType()));
1334
1335 // Extend comparison result to either bool (C++) or int (C).
1336 rewriter.replaceOpWithNewOp<mlir::LLVM::FCmpOp>(castOp, kind, llvmSrcVal,
1337 zeroFloat);
1338
1339 return mlir::success();
1340 }
1341 case cir::CastKind::bool_to_int: {
1342 mlir::Type dstTy = castOp.getType();
1343 mlir::Value llvmSrcVal = adaptor.getSrc();
1344 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1345 // Compare element widths so this also handles vector bool -> int casts.
1346 auto srcElemTy = mlir::cast<mlir::IntegerType>(
1347 elementTypeIfVector(llvmSrcVal.getType()));
1348 auto dstElemTy = mlir::cast<cir::IntType>(elementTypeIfVector(dstTy));
1349
1350 if (srcElemTy.getWidth() == dstElemTy.getWidth())
1351 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(castOp, llvmDstTy,
1352 llvmSrcVal);
1353 else
1354 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(castOp, llvmDstTy,
1355 llvmSrcVal);
1356 return mlir::success();
1357 }
1358 case cir::CastKind::bool_to_float: {
1359 mlir::Type dstTy = castOp.getType();
1360 mlir::Value llvmSrcVal = adaptor.getSrc();
1361 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1362 rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy,
1363 llvmSrcVal);
1364 return mlir::success();
1365 }
1366 case cir::CastKind::int_to_float: {
1367 mlir::Type dstTy = castOp.getType();
1368 mlir::Value llvmSrcVal = adaptor.getSrc();
1369 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1370 if (mlir::cast<cir::IntType>(elementTypeIfVector(castOp.getSrc().getType()))
1371 .isSigned())
1372 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(castOp, llvmDstTy,
1373 llvmSrcVal);
1374 else
1375 rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(castOp, llvmDstTy,
1376 llvmSrcVal);
1377 return mlir::success();
1378 }
1379 case cir::CastKind::float_to_int: {
1380 mlir::Type dstTy = castOp.getType();
1381 mlir::Value llvmSrcVal = adaptor.getSrc();
1382 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1383 if (mlir::cast<cir::IntType>(elementTypeIfVector(castOp.getType()))
1384 .isSigned())
1385 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(castOp, llvmDstTy,
1386 llvmSrcVal);
1387 else
1388 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(castOp, llvmDstTy,
1389 llvmSrcVal);
1390 return mlir::success();
1391 }
1392 case cir::CastKind::bitcast: {
1393 mlir::Type dstTy = castOp.getType();
1394 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1395
1396 assert(!MissingFeatures::cxxABI());
1398
1399 mlir::Value llvmSrcVal = adaptor.getSrc();
1400 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(castOp, llvmDstTy,
1401 llvmSrcVal);
1402 return mlir::success();
1403 }
1404 case cir::CastKind::ptr_to_bool: {
1405 mlir::Value llvmSrcVal = adaptor.getSrc();
1406 mlir::Value zeroPtr = mlir::LLVM::ZeroOp::create(rewriter, castOp.getLoc(),
1407 llvmSrcVal.getType());
1408 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
1409 castOp, mlir::LLVM::ICmpPredicate::ne, llvmSrcVal, zeroPtr);
1410 break;
1411 }
1412 case cir::CastKind::address_space: {
1413 mlir::Type dstTy = castOp.getType();
1414 mlir::Value llvmSrcVal = adaptor.getSrc();
1415 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
1416 rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>(castOp, llvmDstTy,
1417 llvmSrcVal);
1418 break;
1419 }
1420 case cir::CastKind::member_ptr_to_bool:
1421 assert(!MissingFeatures::cxxABI());
1422 assert(!MissingFeatures::methodType());
1423 break;
1424 default: {
1425 return castOp.emitError("Unhandled cast kind: ")
1426 << castOp.getKindAttrName();
1427 }
1428 }
1429
1430 return mlir::success();
1431}
1432
1433static mlir::Value convertToIndexTy(mlir::ConversionPatternRewriter &rewriter,
1434 mlir::ModuleOp mod, mlir::Value index,
1435 mlir::Type baseTy, cir::IntType strideTy) {
1436 mlir::Operation *indexOp = index.getDefiningOp();
1437 if (!indexOp)
1438 return index;
1439
1440 auto indexType = mlir::cast<mlir::IntegerType>(index.getType());
1441 mlir::DataLayout llvmLayout(mod);
1442 std::optional<uint64_t> layoutWidth = llvmLayout.getTypeIndexBitwidth(baseTy);
1443
1444 // If there is no change in width, don't do anything.
1445 if (!layoutWidth || *layoutWidth == indexType.getWidth())
1446 return index;
1447
1448 // If the index comes from a subtraction, make sure the extension happens
1449 // before it. To achieve that, look at unary minus, which already got
1450 // lowered to "sub 0, x".
1451 auto sub = dyn_cast<mlir::LLVM::SubOp>(indexOp);
1452 bool rewriteSub = false;
1453 if (sub) {
1454 if (auto lhsConst =
1455 dyn_cast<mlir::LLVM::ConstantOp>(sub.getLhs().getDefiningOp())) {
1456 auto lhsConstInt = mlir::dyn_cast<mlir::IntegerAttr>(lhsConst.getValue());
1457 if (lhsConstInt && lhsConstInt.getValue() == 0) {
1458 index = sub.getRhs();
1459 rewriteSub = true;
1460 }
1461 }
1462 }
1463
1464 auto llvmDstType = rewriter.getIntegerType(*layoutWidth);
1465 bool isUnsigned = strideTy && strideTy.isUnsigned();
1466 index = getLLVMIntCast(rewriter, index, llvmDstType, isUnsigned,
1467 indexType.getWidth(), *layoutWidth);
1468
1469 if (rewriteSub) {
1470 index = mlir::LLVM::SubOp::create(
1471 rewriter, index.getLoc(),
1472 mlir::LLVM::ConstantOp::create(rewriter, index.getLoc(),
1473 index.getType(), 0),
1474 index);
1475 // TODO: ensure sub is trivially dead now.
1476 rewriter.eraseOp(sub);
1477 }
1478
1479 return index;
1480}
1481
1482mlir::LogicalResult CIRToLLVMPtrStrideOpLowering::matchAndRewrite(
1483 cir::PtrStrideOp ptrStrideOp, OpAdaptor adaptor,
1484 mlir::ConversionPatternRewriter &rewriter) const {
1485
1486 const mlir::TypeConverter *tc = getTypeConverter();
1487 const mlir::Type resultTy = tc->convertType(ptrStrideOp.getType());
1488
1489 mlir::Type elementTy =
1490 convertTypeForMemory(*tc, dataLayout, ptrStrideOp.getElementType());
1491
1492 // void and function types doesn't really have a layout to use in GEPs,
1493 // make it i8 instead.
1494 if (mlir::isa<mlir::LLVM::LLVMVoidType>(elementTy) ||
1495 mlir::isa<mlir::LLVM::LLVMFunctionType>(elementTy))
1496 elementTy = mlir::IntegerType::get(elementTy.getContext(), 8,
1497 mlir::IntegerType::Signless);
1498 // Zero-extend, sign-extend or trunc the pointer value.
1499 mlir::Value index = adaptor.getStride();
1500 index = convertToIndexTy(
1501 rewriter, ptrStrideOp->getParentOfType<mlir::ModuleOp>(), index,
1502 adaptor.getBase().getType(),
1503 dyn_cast<cir::IntType>(ptrStrideOp.getOperand(1).getType()));
1504
1505 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1506 ptrStrideOp, resultTy, elementTy, adaptor.getBase(), index);
1507 return mlir::success();
1508}
1509
1510mlir::LogicalResult CIRToLLVMGetElementOpLowering::matchAndRewrite(
1511 cir::GetElementOp op, OpAdaptor adaptor,
1512 mlir::ConversionPatternRewriter &rewriter) const {
1513 if (auto arrayTy =
1514 mlir::dyn_cast<cir::ArrayType>(op.getBaseType().getPointee())) {
1515 const mlir::TypeConverter *converter = getTypeConverter();
1516 const mlir::Type llArrayTy = converter->convertType(arrayTy);
1517 const mlir::Type llResultTy = converter->convertType(op.getType());
1518 mlir::Type elementTy =
1519 convertTypeForMemory(*converter, dataLayout, op.getElementType());
1520
1521 // void and function types don't really have a layout to use in GEPs,
1522 // make it i8 instead.
1523 if (mlir::isa<mlir::LLVM::LLVMVoidType>(elementTy) ||
1524 mlir::isa<mlir::LLVM::LLVMFunctionType>(elementTy))
1525 elementTy = rewriter.getIntegerType(8);
1526
1527 mlir::Value index = adaptor.getIndex();
1528 index =
1529 convertToIndexTy(rewriter, op->getParentOfType<mlir::ModuleOp>(), index,
1530 adaptor.getBase().getType(),
1531 dyn_cast<cir::IntType>(op.getOperand(1).getType()));
1532
1533 // Since the base address is a pointer to an aggregate, the first
1534 // offset is always zero. The second offset tell us which member it
1535 // will access.
1536 std::array<mlir::LLVM::GEPArg, 2> offset{0, index};
1537 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(op, llResultTy, llArrayTy,
1538 adaptor.getBase(), offset);
1539 return mlir::success();
1540 }
1541
1542 op.emitError() << "NYI: GetElementOp lowering to LLVM for non-array";
1543 return mlir::failure();
1544}
1545
1546mlir::LogicalResult CIRToLLVMBaseClassAddrOpLowering::matchAndRewrite(
1547 cir::BaseClassAddrOp baseClassOp, OpAdaptor adaptor,
1548 mlir::ConversionPatternRewriter &rewriter) const {
1549 const mlir::Type resultType =
1550 getTypeConverter()->convertType(baseClassOp.getType());
1551 mlir::Value derivedAddr = adaptor.getDerivedAddr();
1552 llvm::SmallVector<mlir::LLVM::GEPArg, 1> offset = {
1553 adaptor.getOffset().getZExtValue()};
1554 mlir::Type byteType = mlir::IntegerType::get(resultType.getContext(), 8,
1555 mlir::IntegerType::Signless);
1556 if (adaptor.getOffset().getZExtValue() == 0) {
1557 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(
1558 baseClassOp, resultType, adaptor.getDerivedAddr());
1559 return mlir::success();
1560 }
1561
1562 if (baseClassOp.getAssumeNotNull()) {
1563 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1564 baseClassOp, resultType, byteType, derivedAddr, offset);
1565 } else {
1566 auto loc = baseClassOp.getLoc();
1567 mlir::Value isNull = mlir::LLVM::ICmpOp::create(
1568 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, derivedAddr,
1569 mlir::LLVM::ZeroOp::create(rewriter, loc, derivedAddr.getType()));
1570 mlir::Value adjusted = mlir::LLVM::GEPOp::create(
1571 rewriter, loc, resultType, byteType, derivedAddr, offset);
1572 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(baseClassOp, isNull,
1573 derivedAddr, adjusted);
1574 }
1575 return mlir::success();
1576}
1577
1578mlir::LogicalResult CIRToLLVMDerivedClassAddrOpLowering::matchAndRewrite(
1579 cir::DerivedClassAddrOp derivedClassOp, OpAdaptor adaptor,
1580 mlir::ConversionPatternRewriter &rewriter) const {
1581 const mlir::Type resultType =
1582 getTypeConverter()->convertType(derivedClassOp.getType());
1583 mlir::Value baseAddr = adaptor.getBaseAddr();
1584 // The offset is set in the operation as an unsigned value, but it must be
1585 // applied as a negative offset.
1586 int64_t offsetVal = -(adaptor.getOffset().getZExtValue());
1587 if (offsetVal == 0) {
1588 // If the offset is zero, we can just return the base address,
1589 rewriter.replaceOp(derivedClassOp, baseAddr);
1590 return mlir::success();
1591 }
1592 llvm::SmallVector<mlir::LLVM::GEPArg, 1> offset = {offsetVal};
1593 mlir::Type byteType = mlir::IntegerType::get(resultType.getContext(), 8,
1594 mlir::IntegerType::Signless);
1595 if (derivedClassOp.getAssumeNotNull()) {
1596 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
1597 derivedClassOp, resultType, byteType, baseAddr, offset,
1598 mlir::LLVM::GEPNoWrapFlags::inbounds);
1599 } else {
1600 mlir::Location loc = derivedClassOp.getLoc();
1601 mlir::Value isNull = mlir::LLVM::ICmpOp::create(
1602 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, baseAddr,
1603 mlir::LLVM::ZeroOp::create(rewriter, loc, baseAddr.getType()));
1604 mlir::Value adjusted =
1605 mlir::LLVM::GEPOp::create(rewriter, loc, resultType, byteType, baseAddr,
1606 offset, mlir::LLVM::GEPNoWrapFlags::inbounds);
1607 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(derivedClassOp, isNull,
1608 baseAddr, adjusted);
1609 }
1610 return mlir::success();
1611}
1612
1613mlir::LogicalResult CIRToLLVMFMaxNumOpLowering::matchAndRewrite(
1614 cir::FMaxNumOp op, OpAdaptor adaptor,
1615 mlir::ConversionPatternRewriter &rewriter) const {
1616 mlir::Type resTy = typeConverter->convertType(op.getType());
1617 rewriter.replaceOpWithNewOp<mlir::LLVM::MaxNumOp>(
1618 op, resTy, adaptor.getLhs(), adaptor.getRhs(),
1619 mlir::LLVM::FastmathFlags::nsz);
1620 return mlir::success();
1621}
1622
1623mlir::LogicalResult CIRToLLVMFMinNumOpLowering::matchAndRewrite(
1624 cir::FMinNumOp op, OpAdaptor adaptor,
1625 mlir::ConversionPatternRewriter &rewriter) const {
1626 mlir::Type resTy = typeConverter->convertType(op.getType());
1627 rewriter.replaceOpWithNewOp<mlir::LLVM::MinNumOp>(
1628 op, resTy, adaptor.getLhs(), adaptor.getRhs(),
1629 mlir::LLVM::FastmathFlags::nsz);
1630 return mlir::success();
1631}
1632
1633mlir::LogicalResult CIRToLLVMAllocaOpLowering::matchAndRewrite(
1634 cir::AllocaOp op, OpAdaptor adaptor,
1635 mlir::ConversionPatternRewriter &rewriter) const {
1636 mlir::Value size =
1637 op.isDynamic()
1638 ? adaptor.getDynAllocSize()
1639 : mlir::LLVM::ConstantOp::create(
1640 rewriter, op.getLoc(),
1641 typeConverter->convertType(rewriter.getIndexType()), 1);
1642 mlir::Type elementTy =
1643 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getAllocaType());
1644 mlir::Type resultTy =
1645 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getType());
1646
1649
1650 rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(op, resultTy, elementTy,
1651 size, op.getAlignment());
1652
1653 return mlir::success();
1654}
1655
1656mlir::LogicalResult CIRToLLVMRotateOpLowering::matchAndRewrite(
1657 cir::RotateOp op, OpAdaptor adaptor,
1658 mlir::ConversionPatternRewriter &rewriter) const {
1659 // Note that LLVM intrinsic calls to @llvm.fsh{r,l}.i* have the same type as
1660 // the operand.
1661 mlir::Value input = adaptor.getInput();
1662 if (op.isRotateLeft())
1663 rewriter.replaceOpWithNewOp<mlir::LLVM::FshlOp>(op, input, input,
1664 adaptor.getAmount());
1665 else
1666 rewriter.replaceOpWithNewOp<mlir::LLVM::FshrOp>(op, input, input,
1667 adaptor.getAmount());
1668 return mlir::LogicalResult::success();
1669}
1670
1671static void lowerCallAttributes(cir::CIRCallOpInterface op,
1673 for (mlir::NamedAttribute attr : op->getAttrs()) {
1674 if (attr.getName() == CIRDialect::getCalleeAttrName() ||
1675 attr.getName() == CIRDialect::getSideEffectAttrName() ||
1676 attr.getName() == CIRDialect::getNoThrowAttrName() ||
1677 attr.getName() == CIRDialect::getNoUnwindAttrName() ||
1678 attr.getName() == CIRDialect::getNoReturnAttrName() ||
1679 attr.getName() == CIRDialect::getMustTailAttrName())
1680 continue;
1681
1683 result.push_back(attr);
1684 }
1685}
1686
1687static mlir::LogicalResult
1688rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
1689 mlir::ConversionPatternRewriter &rewriter,
1690 const mlir::TypeConverter *converter,
1691 mlir::SymbolTableCollection &symbolTables,
1692 mlir::FlatSymbolRefAttr calleeAttr,
1693 mlir::Block *continueBlock = nullptr,
1694 mlir::Block *landingPadBlock = nullptr) {
1696 mlir::ValueTypeRange<mlir::ResultRange> cirResults = op->getResultTypes();
1697 auto call = cast<cir::CIRCallOpInterface>(op);
1698
1699 if (converter->convertTypes(cirResults, llvmResults).failed())
1700 return mlir::failure();
1701
1703
1704 mlir::LLVM::MemoryEffectsAttr memoryEffects;
1705 bool noUnwind = false;
1706 bool willReturn = false;
1707 bool noReturn = false;
1708 convertSideEffectForCall(op, call.getNothrow(), call.getSideEffect(),
1709 memoryEffects, noUnwind, willReturn, noReturn);
1710
1712 lowerCallAttributes(call, attributes);
1713
1714 mlir::LLVM::LLVMFunctionType llvmFnTy;
1715
1716 // Temporary to handle the case where we need to prepend an operand if the
1717 // callee is an alias.
1718 SmallVector<mlir::Value> adjustedCallOperands;
1719
1720 if (calleeAttr) { // direct call
1721 mlir::Operation *callee =
1722 symbolTables.lookupNearestSymbolFrom(op, calleeAttr);
1723 if (auto fn = mlir::dyn_cast<mlir::FunctionOpInterface>(callee)) {
1724 llvmFnTy = converter->convertType<mlir::LLVM::LLVMFunctionType>(
1725 fn.getFunctionType());
1726 assert(llvmFnTy && "Failed to convert function type");
1727 } else if (auto alias = mlir::cast<mlir::LLVM::AliasOp>(callee)) {
1728 // If the callee was an alias. In that case,
1729 // we need to prepend the address of the alias to the operands. The
1730 // way aliases work in the LLVM dialect is a little counter-intuitive.
1731 // The AliasOp itself is a pseudo-function that returns the address of
1732 // the global value being aliased, but when we generate the call we
1733 // need to insert an operation that gets the address of the AliasOp.
1734 // This all gets sorted out when the LLVM dialect is lowered to LLVM IR.
1735 auto symAttr = mlir::cast<mlir::FlatSymbolRefAttr>(calleeAttr);
1736 auto addrOfAlias =
1737 mlir::LLVM::AddressOfOp::create(
1738 rewriter, op->getLoc(),
1739 mlir::LLVM::LLVMPointerType::get(rewriter.getContext()), symAttr)
1740 .getResult();
1741 adjustedCallOperands.push_back(addrOfAlias);
1742
1743 // Now add the regular operands and assign this to the range value.
1744 llvm::append_range(adjustedCallOperands, callOperands);
1745 callOperands = adjustedCallOperands;
1746
1747 // Clear the callee attribute because we're calling an alias.
1748 calleeAttr = {};
1749 llvmFnTy = mlir::cast<mlir::LLVM::LLVMFunctionType>(alias.getType());
1750 } else {
1751 // Was this an ifunc?
1752 return op->emitError("Unexpected callee type!");
1753 }
1754 } else { // indirect call
1755 assert(!op->getOperands().empty() &&
1756 "operands list must no be empty for the indirect call");
1757 auto calleeTy = op->getOperands().front().getType();
1758 auto calleePtrTy = cast<cir::PointerType>(calleeTy);
1759 auto calleeFuncTy = cast<cir::FuncType>(calleePtrTy.getPointee());
1760 llvm::append_range(adjustedCallOperands, callOperands);
1761 llvmFnTy = cast<mlir::LLVM::LLVMFunctionType>(
1762 converter->convertType(calleeFuncTy));
1763 }
1764
1766
1767 if (landingPadBlock) {
1768 auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::InvokeOp>(
1769 op, llvmFnTy, calleeAttr, callOperands, continueBlock,
1770 mlir::ValueRange{}, landingPadBlock, mlir::ValueRange{});
1771 newOp->setAttrs(attributes);
1772 } else {
1773 auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
1774 op, llvmFnTy, calleeAttr, callOperands);
1775 newOp->setAttrs(attributes);
1776 if (memoryEffects)
1777 newOp.setMemoryEffectsAttr(memoryEffects);
1778 newOp.setNoUnwind(noUnwind);
1779 newOp.setWillReturn(willReturn);
1780 newOp.setNoreturn(noReturn);
1781 if (op->hasAttr(CIRDialect::getMustTailAttrName()))
1782 newOp.setTailCallKind(mlir::LLVM::TailCallKind::MustTail);
1783 }
1784
1785 return mlir::success();
1786}
1787
1788mlir::LogicalResult CIRToLLVMCallOpLowering::matchAndRewrite(
1789 cir::CallOp op, OpAdaptor adaptor,
1790 mlir::ConversionPatternRewriter &rewriter) const {
1791 return rewriteCallOrInvoke(op.getOperation(), adaptor.getOperands(), rewriter,
1792 getTypeConverter(), symbolTables,
1793 op.getCalleeAttr());
1794}
1795
1796mlir::LogicalResult CIRToLLVMTryCallOpLowering::matchAndRewrite(
1797 cir::TryCallOp op, OpAdaptor adaptor,
1798 mlir::ConversionPatternRewriter &rewriter) const {
1800 return rewriteCallOrInvoke(
1801 op.getOperation(), adaptor.getOperands(), rewriter, getTypeConverter(),
1802 symbolTables, op.getCalleeAttr(), op.getNormalDest(), op.getUnwindDest());
1803}
1804
1805mlir::LogicalResult CIRToLLVMReturnAddrOpLowering::matchAndRewrite(
1806 cir::ReturnAddrOp op, OpAdaptor adaptor,
1807 mlir::ConversionPatternRewriter &rewriter) const {
1808 const mlir::Type llvmPtrTy = getTypeConverter()->convertType(op.getType());
1809 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.returnaddress",
1810 llvmPtrTy, adaptor.getOperands());
1811 return mlir::success();
1812}
1813
1814mlir::LogicalResult CIRToLLVMFrameAddrOpLowering::matchAndRewrite(
1815 cir::FrameAddrOp op, OpAdaptor adaptor,
1816 mlir::ConversionPatternRewriter &rewriter) const {
1817 const mlir::Type llvmPtrTy = getTypeConverter()->convertType(op.getType());
1818 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.frameaddress", llvmPtrTy,
1819 adaptor.getOperands());
1820 return mlir::success();
1821}
1822
1823mlir::LogicalResult CIRToLLVMClearCacheOpLowering::matchAndRewrite(
1824 cir::ClearCacheOp op, OpAdaptor adaptor,
1825 mlir::ConversionPatternRewriter &rewriter) const {
1826 mlir::Value begin = adaptor.getBegin();
1827 mlir::Value end = adaptor.getEnd();
1828 auto intrinNameAttr =
1829 mlir::StringAttr::get(op.getContext(), "llvm.clear_cache");
1830 rewriter.replaceOpWithNewOp<mlir::LLVM::CallIntrinsicOp>(
1831 op, mlir::Type{}, intrinNameAttr, mlir::ValueRange{begin, end});
1832
1833 return mlir::success();
1834}
1835
1836mlir::LogicalResult CIRToLLVMAddrOfReturnAddrOpLowering::matchAndRewrite(
1837 cir::AddrOfReturnAddrOp op, OpAdaptor adaptor,
1838 mlir::ConversionPatternRewriter &rewriter) const {
1839 const mlir::Type llvmPtrTy = getTypeConverter()->convertType(op.getType());
1840 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.addressofreturnaddress",
1841 llvmPtrTy, adaptor.getOperands());
1842 return mlir::success();
1843}
1844
1845mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite(
1846 cir::LoadOp op, OpAdaptor adaptor,
1847 mlir::ConversionPatternRewriter &rewriter) const {
1848 const mlir::Type llvmTy =
1849 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getType());
1850 mlir::LLVM::AtomicOrdering ordering = getLLVMMemOrder(op.getMemOrder());
1851 std::optional<size_t> opAlign = op.getAlignment();
1852 unsigned alignment =
1853 (unsigned)opAlign.value_or(dataLayout.getTypeABIAlignment(llvmTy));
1854
1856
1857 // TODO: nontemporal.
1859
1860 std::optional<llvm::StringRef> llvmSyncScope =
1861 getLLVMSyncScope(op.getSyncScope());
1862
1863 mlir::LLVM::LoadOp newLoad = mlir::LLVM::LoadOp::create(
1864 rewriter, op->getLoc(), llvmTy, adaptor.getAddr(), alignment,
1865 op.getIsVolatile(), /*isNonTemporal=*/false,
1866 /*isInvariant=*/false, /*isInvariantGroup=*/false, ordering,
1867 llvmSyncScope.value_or(std::string()));
1868
1869 // Convert adapted result to its original type if needed.
1870 mlir::Value result =
1871 emitFromMemory(rewriter, dataLayout, op, newLoad.getResult());
1872 rewriter.replaceOp(op, result);
1874 return mlir::LogicalResult::success();
1875}
1876
1877mlir::LogicalResult
1878cir::direct::CIRToLLVMVecMaskedLoadOpLowering::matchAndRewrite(
1879 cir::VecMaskedLoadOp op, OpAdaptor adaptor,
1880 mlir::ConversionPatternRewriter &rewriter) const {
1881 const mlir::Type llvmResTy =
1882 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getType());
1883
1884 std::optional<size_t> opAlign = op.getAlignment();
1885 unsigned alignment =
1886 (unsigned)opAlign.value_or(dataLayout.getTypeABIAlignment(llvmResTy));
1887
1888 mlir::IntegerAttr alignAttr = rewriter.getI32IntegerAttr(alignment);
1889
1890 auto newLoad = mlir::LLVM::MaskedLoadOp::create(
1891 rewriter, op.getLoc(), llvmResTy, adaptor.getAddr(), adaptor.getMask(),
1892 adaptor.getPassThru(), alignAttr);
1893
1894 rewriter.replaceOp(op, newLoad.getResult());
1895 return mlir::success();
1896}
1897
1898mlir::LogicalResult CIRToLLVMStoreOpLowering::matchAndRewrite(
1899 cir::StoreOp op, OpAdaptor adaptor,
1900 mlir::ConversionPatternRewriter &rewriter) const {
1901 mlir::LLVM::AtomicOrdering memorder = getLLVMMemOrder(op.getMemOrder());
1902 const mlir::Type llvmTy =
1903 getTypeConverter()->convertType(op.getValue().getType());
1904 std::optional<size_t> opAlign = op.getAlignment();
1905 unsigned alignment =
1906 (unsigned)opAlign.value_or(dataLayout.getTypeABIAlignment(llvmTy));
1907
1909
1910 // Convert adapted value to its memory type if needed.
1911 mlir::Value value = emitToMemory(rewriter, dataLayout,
1912 op.getValue().getType(), adaptor.getValue());
1913 // TODO: nontemporal.
1916
1917 std::optional<llvm::StringRef> llvmSyncScope =
1918 getLLVMSyncScope(op.getSyncScope());
1919
1920 mlir::LLVM::StoreOp storeOp = mlir::LLVM::StoreOp::create(
1921 rewriter, op->getLoc(), value, adaptor.getAddr(), alignment,
1922 op.getIsVolatile(),
1923 /*isNonTemporal=*/false, /*isInvariantGroup=*/false, memorder,
1924 llvmSyncScope.value_or(std::string()));
1925 rewriter.replaceOp(op, storeOp);
1927 return mlir::LogicalResult::success();
1928}
1929
1930static mlir::Type getConstArrayBaseElementType(mlir::Type ty) {
1931 while (auto arrTy = mlir::dyn_cast<cir::ArrayType>(ty))
1932 ty = arrTy.getElementType();
1933 return ty;
1934}
1935
1936static bool isBulkLowerableConstArrayBaseElement(mlir::Type baseElemTy) {
1937 return mlir::isa<cir::PointerType, cir::IntType, cir::BoolType,
1938 cir::FPTypeInterface>(baseElemTy);
1939}
1940
1941mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
1942 cir::ConstantOp op, OpAdaptor adaptor,
1943 mlir::ConversionPatternRewriter &rewriter) const {
1944 mlir::Attribute attr = op.getValue();
1945
1946 if (mlir::isa<cir::PoisonAttr>(attr)) {
1947 rewriter.replaceOpWithNewOp<mlir::LLVM::PoisonOp>(
1948 op, getTypeConverter()->convertType(op.getType()));
1949 return mlir::success();
1950 }
1951
1952 if (mlir::isa<cir::UndefAttr>(attr)) {
1953 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
1954 op, getTypeConverter()->convertType(op.getType()));
1955 return mlir::success();
1956 }
1957
1958 if (mlir::isa<mlir::IntegerType>(op.getType())) {
1959 // Verified cir.const operations cannot actually be of these types, but the
1960 // lowering pass may generate temporary cir.const operations with these
1961 // types. This is OK since MLIR allows unverified operations to be alive
1962 // during a pass as long as they don't live past the end of the pass.
1963 attr = op.getValue();
1964 } else if (mlir::isa<cir::BoolType>(op.getType())) {
1965 int value = mlir::cast<cir::BoolAttr>(op.getValue()).getValue();
1966 attr = rewriter.getIntegerAttr(typeConverter->convertType(op.getType()),
1967 value);
1968 } else if (mlir::isa<cir::IntType>(op.getType())) {
1969 // Lower GlobalViewAttr to llvm.mlir.addressof + llvm.mlir.ptrtoint
1970 if (auto ga = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) {
1971 // We can have a global view with an integer type in the case of method
1972 // pointers, but the lowering of those doesn't go through this path.
1973 // They are handled in the visitCirAttr. This is left as an error until
1974 // we have a test case that reaches it.
1976 op.emitError() << "global view with integer type";
1977 return mlir::failure();
1978 }
1979
1980 attr = rewriter.getIntegerAttr(
1981 typeConverter->convertType(op.getType()),
1982 mlir::cast<cir::IntAttr>(op.getValue()).getValue());
1983 } else if (mlir::isa<cir::FPTypeInterface>(op.getType())) {
1984 attr = rewriter.getFloatAttr(
1985 typeConverter->convertType(op.getType()),
1986 mlir::cast<cir::FPAttr>(op.getValue()).getValue());
1987 } else if (mlir::isa<cir::PointerType>(op.getType())) {
1988 // Optimize with dedicated LLVM op for null pointers.
1989 if (mlir::isa<cir::ConstPtrAttr>(op.getValue())) {
1990 if (mlir::cast<cir::ConstPtrAttr>(op.getValue()).isNullValue()) {
1991 rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(
1992 op, typeConverter->convertType(op.getType()));
1993 return mlir::success();
1994 }
1995 }
1996 // Lower GlobalViewAttr to llvm.mlir.addressof
1997 if (auto gv = mlir::dyn_cast<cir::GlobalViewAttr>(op.getValue())) {
1998 auto newOp = lowerCirAttrAsValue(op, gv, rewriter, getTypeConverter());
1999 rewriter.replaceOp(op, newOp);
2000 return mlir::success();
2001 }
2002 attr = op.getValue();
2003 } else if (const auto arrTy = mlir::dyn_cast<cir::ArrayType>(op.getType())) {
2004 const auto constArr = mlir::dyn_cast<cir::ConstArrayAttr>(op.getValue());
2005 if (!constArr && !isa<cir::ZeroAttr, cir::UndefAttr>(op.getValue()))
2006 return op.emitError() << "array does not have a constant initializer";
2007
2008 std::optional<mlir::Attribute> denseAttr;
2009 if (constArr &&
2010 (denseAttr = lowerConstArrayAttr(constArr, typeConverter))) {
2011 attr = denseAttr.value();
2012 } else {
2013 const mlir::Value initVal =
2014 lowerCirAttrAsValue(op, op.getValue(), rewriter, typeConverter);
2015 rewriter.replaceOp(op, initVal);
2016 return mlir::success();
2017 }
2018 } else if (const auto recordAttr =
2019 mlir::dyn_cast<cir::ConstRecordAttr>(op.getValue())) {
2020 auto initVal = lowerCirAttrAsValue(op, recordAttr, rewriter, typeConverter);
2021 rewriter.replaceOp(op, initVal);
2022 return mlir::success();
2023 } else if (const auto vecTy = mlir::dyn_cast<cir::VectorType>(op.getType())) {
2024 rewriter.replaceOp(op, lowerCirAttrAsValue(op, op.getValue(), rewriter,
2025 getTypeConverter()));
2026 return mlir::success();
2027 } else if (mlir::isa<cir::RecordType>(op.getType())) {
2028 if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(attr)) {
2029 mlir::Value initVal =
2030 lowerCirAttrAsValue(op, attr, rewriter, typeConverter);
2031 rewriter.replaceOp(op, initVal);
2032 return mlir::success();
2033 }
2034 return op.emitError() << "unsupported lowering for record constant type "
2035 << op.getType();
2036 } else if (auto complexTy = mlir::dyn_cast<cir::ComplexType>(op.getType())) {
2037 mlir::Type complexElemTy = complexTy.getElementType();
2038 mlir::Type complexElemLLVMTy = typeConverter->convertType(complexElemTy);
2039
2040 if (auto zeroInitAttr = mlir::dyn_cast<cir::ZeroAttr>(op.getValue())) {
2041 mlir::TypedAttr zeroAttr = rewriter.getZeroAttr(complexElemLLVMTy);
2042 mlir::ArrayAttr array = rewriter.getArrayAttr({zeroAttr, zeroAttr});
2043 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
2044 op, getTypeConverter()->convertType(op.getType()), array);
2045 return mlir::success();
2046 }
2047
2048 if (mlir::isa<cir::UndefAttr>(op.getValue())) {
2049 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
2050 op, getTypeConverter()->convertType(op.getType()));
2051 return mlir::success();
2052 }
2053
2054 auto complexAttr = mlir::cast<cir::ConstComplexAttr>(op.getValue());
2055
2056 mlir::Attribute components[2];
2057 if (mlir::isa<cir::IntType>(complexElemTy)) {
2058 components[0] = rewriter.getIntegerAttr(
2059 complexElemLLVMTy,
2060 mlir::cast<cir::IntAttr>(complexAttr.getReal()).getValue());
2061 components[1] = rewriter.getIntegerAttr(
2062 complexElemLLVMTy,
2063 mlir::cast<cir::IntAttr>(complexAttr.getImag()).getValue());
2064 } else {
2065 components[0] = rewriter.getFloatAttr(
2066 complexElemLLVMTy,
2067 mlir::cast<cir::FPAttr>(complexAttr.getReal()).getValue());
2068 components[1] = rewriter.getFloatAttr(
2069 complexElemLLVMTy,
2070 mlir::cast<cir::FPAttr>(complexAttr.getImag()).getValue());
2071 }
2072
2073 attr = rewriter.getArrayAttr(components);
2074 } else {
2075 return op.emitError() << "unsupported constant type " << op.getType();
2076 }
2077
2078 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
2079 op, getTypeConverter()->convertType(op.getType()), attr);
2080
2081 return mlir::success();
2082}
2083
2084static uint64_t getTypeSize(mlir::Type type, mlir::Operation &op) {
2085 mlir::DataLayout layout(op.getParentOfType<mlir::ModuleOp>());
2086 // For LLVM purposes we treat void as u8.
2087 if (isa<cir::VoidType>(type))
2088 type = cir::IntType::get(type.getContext(), 8, /*isSigned=*/false);
2089 return llvm::divideCeil(layout.getTypeSizeInBits(type), 8);
2090}
2091
2092mlir::LogicalResult CIRToLLVMPrefetchOpLowering::matchAndRewrite(
2093 cir::PrefetchOp op, OpAdaptor adaptor,
2094 mlir::ConversionPatternRewriter &rewriter) const {
2095 rewriter.replaceOpWithNewOp<mlir::LLVM::Prefetch>(
2096 op, adaptor.getAddr(), adaptor.getIsWrite(), adaptor.getLocality(),
2097 /*DataCache=*/1);
2098 return mlir::success();
2099}
2100
2101mlir::LogicalResult CIRToLLVMPtrDiffOpLowering::matchAndRewrite(
2102 cir::PtrDiffOp op, OpAdaptor adaptor,
2103 mlir::ConversionPatternRewriter &rewriter) const {
2104 auto dstTy = mlir::cast<cir::IntType>(op.getType());
2105 mlir::Type llvmDstTy = getTypeConverter()->convertType(dstTy);
2106
2107 auto lhs = mlir::LLVM::PtrToIntOp::create(rewriter, op.getLoc(), llvmDstTy,
2108 adaptor.getLhs());
2109 auto rhs = mlir::LLVM::PtrToIntOp::create(rewriter, op.getLoc(), llvmDstTy,
2110 adaptor.getRhs());
2111
2112 auto diff =
2113 mlir::LLVM::SubOp::create(rewriter, op.getLoc(), llvmDstTy, lhs, rhs);
2114
2115 cir::PointerType ptrTy = op.getLhs().getType();
2117 uint64_t typeSize = getTypeSize(ptrTy.getPointee(), *op);
2118
2119 // Avoid silly division by 1.
2120 mlir::Value resultVal = diff.getResult();
2121 if (typeSize != 1) {
2122 auto typeSizeVal = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
2123 llvmDstTy, typeSize);
2124
2125 if (dstTy.isUnsigned()) {
2126 auto uDiv =
2127 mlir::LLVM::UDivOp::create(rewriter, op.getLoc(), diff, typeSizeVal);
2128 uDiv.setIsExact(true);
2129 resultVal = uDiv.getResult();
2130 } else {
2131 auto sDiv =
2132 mlir::LLVM::SDivOp::create(rewriter, op.getLoc(), diff, typeSizeVal);
2133 sDiv.setIsExact(true);
2134 resultVal = sDiv.getResult();
2135 }
2136 }
2137 rewriter.replaceOp(op, resultVal);
2138 return mlir::success();
2139}
2140
2141mlir::LogicalResult CIRToLLVMExpectOpLowering::matchAndRewrite(
2142 cir::ExpectOp op, OpAdaptor adaptor,
2143 mlir::ConversionPatternRewriter &rewriter) const {
2144 // TODO(cir): do not generate LLVM intrinsics under -O0
2146
2147 std::optional<llvm::APFloat> prob = op.getProb();
2148 if (prob)
2149 rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectWithProbabilityOp>(
2150 op, adaptor.getVal(), adaptor.getExpected(), prob.value());
2151 else
2152 rewriter.replaceOpWithNewOp<mlir::LLVM::ExpectOp>(op, adaptor.getVal(),
2153 adaptor.getExpected());
2154 return mlir::success();
2155}
2156
2157mlir::LogicalResult CIRToLLVMAbsOpLowering::matchAndRewrite(
2158 cir::AbsOp op, OpAdaptor adaptor,
2159 mlir::ConversionPatternRewriter &rewriter) const {
2160 mlir::Type resTy = typeConverter->convertType(op.getType());
2161 auto absOp = mlir::LLVM::AbsOp::create(rewriter, op.getLoc(), resTy,
2162 adaptor.getOperands()[0],
2163 adaptor.getMinIsPoison());
2164 rewriter.replaceOp(op, absOp);
2165 return mlir::success();
2166}
2167
2168/// Convert the `cir.func` attributes to `llvm.func` attributes.
2169/// Only retain those attributes that are not constructed by
2170/// `LLVMFuncOp::build`. If `filterArgAttrs` is set, also filter out
2171/// argument attributes.
2172void CIRToLLVMFuncOpLowering::lowerFuncAttributes(
2173 cir::FuncOp func, bool filterArgAndResAttrs,
2174 SmallVectorImpl<mlir::NamedAttribute> &result) const {
2175 for (mlir::NamedAttribute attr : func->getAttrs()) {
2176 if (attr.getName() == mlir::SymbolTable::getSymbolAttrName() ||
2177 attr.getName() == func.getFunctionTypeAttrName() ||
2178 attr.getName() == getLinkageAttrNameString() ||
2179 attr.getName() == func.getCallingConvAttrName() ||
2180 attr.getName() == func.getDsoLocalAttrName() ||
2181 attr.getName() == func.getInlineKindAttrName() ||
2182 attr.getName() == func.getSideEffectAttrName() ||
2183 attr.getName() == CIRDialect::getNoReturnAttrName() ||
2184 attr.getName() == func.getAnnotationsAttrName() ||
2185 (filterArgAndResAttrs &&
2186 (attr.getName() == func.getArgAttrsAttrName() ||
2187 attr.getName() == func.getResAttrsAttrName())))
2188 continue;
2189
2191 result.push_back(attr);
2192 }
2193}
2194
2195mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewriteAlias(
2196 cir::FuncOp op, llvm::StringRef aliasee, mlir::Type ty, OpAdaptor adaptor,
2197 mlir::ConversionPatternRewriter &rewriter) const {
2198 SmallVector<mlir::NamedAttribute, 4> attributes;
2199 lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
2200
2201 mlir::Location loc = op.getLoc();
2202 auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
2203 op, ty, convertLinkage(op.getLinkage()), op.getName(), op.getDsoLocal(),
2204 /*threadLocal=*/false, attributes);
2205
2206 // Create the alias body
2207 mlir::OpBuilder builder(op.getContext());
2208 mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
2209 builder.setInsertionPointToStart(block);
2210 // The type of AddressOfOp is always a pointer.
2212 mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get(ty.getContext());
2213 auto addrOp = mlir::LLVM::AddressOfOp::create(builder, loc, ptrTy, aliasee);
2214 mlir::LLVM::ReturnOp::create(builder, loc, addrOp);
2215
2216 return mlir::success();
2217}
2218
2219mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
2220 cir::FuncOp op, OpAdaptor adaptor,
2221 mlir::ConversionPatternRewriter &rewriter) const {
2222
2223 cir::FuncType fnType = op.getFunctionType();
2224 bool isDsoLocal = op.getDsoLocal();
2225 mlir::TypeConverter::SignatureConversion signatureConversion(
2226 fnType.getNumInputs());
2227
2228 for (const auto &argType : llvm::enumerate(fnType.getInputs())) {
2229 mlir::Type convertedType = typeConverter->convertType(argType.value());
2230 if (!convertedType)
2231 return mlir::failure();
2232 signatureConversion.addInputs(argType.index(), convertedType);
2233 }
2234
2235 mlir::Type resultType =
2236 getTypeConverter()->convertType(fnType.getReturnType());
2237
2238 // Create the LLVM function operation.
2239 mlir::Type llvmFnTy = mlir::LLVM::LLVMFunctionType::get(
2240 resultType ? resultType : mlir::LLVM::LLVMVoidType::get(getContext()),
2241 signatureConversion.getConvertedTypes(),
2242 /*isVarArg=*/fnType.isVarArg());
2243
2244 // If this is an alias, it needs to be lowered to llvm::AliasOp.
2245 if (std::optional<llvm::StringRef> aliasee = op.getAliasee())
2246 return matchAndRewriteAlias(op, *aliasee, llvmFnTy, adaptor, rewriter);
2247
2248 // LLVMFuncOp expects a single FileLine Location instead of a fused
2249 // location.
2250 mlir::Location loc = op.getLoc();
2251 if (mlir::FusedLoc fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(loc))
2252 loc = fusedLoc.getLocations()[0];
2253 assert((mlir::isa<mlir::FileLineColLoc>(loc) ||
2254 mlir::isa<mlir::UnknownLoc>(loc)) &&
2255 "expected single location or unknown location here");
2256
2257 mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
2258 mlir::LLVM::CConv cconv = convertCallingConv(op.getCallingConv());
2259 SmallVector<mlir::NamedAttribute, 4> attributes;
2260 lowerFuncAttributes(op, /*filterArgAndResAttrs=*/false, attributes);
2261
2262 mlir::LLVM::LLVMFuncOp fn = mlir::LLVM::LLVMFuncOp::create(
2263 rewriter, loc, op.getName(), llvmFnTy, linkage, isDsoLocal, cconv,
2264 mlir::SymbolRefAttr(), attributes);
2265
2267
2268 if (std::optional<cir::SideEffect> sideEffectKind = op.getSideEffect()) {
2269 switch (*sideEffectKind) {
2270 case cir::SideEffect::All:
2271 break;
2272 case cir::SideEffect::Pure:
2273 fn.setMemoryEffectsAttr(mlir::LLVM::MemoryEffectsAttr::get(
2274 fn.getContext(),
2275 /*other=*/mlir::LLVM::ModRefInfo::Ref,
2276 /*argMem=*/mlir::LLVM::ModRefInfo::Ref,
2277 /*inaccessibleMem=*/mlir::LLVM::ModRefInfo::Ref,
2278 /*errnoMem=*/mlir::LLVM::ModRefInfo::Ref,
2279 /*targetMem0=*/mlir::LLVM::ModRefInfo::Ref,
2280 /*targetMem1=*/mlir::LLVM::ModRefInfo::Ref));
2281 fn.setNoUnwind(true);
2282 fn.setWillReturn(true);
2283 break;
2284 case cir::SideEffect::Const:
2285 fn.setMemoryEffectsAttr(mlir::LLVM::MemoryEffectsAttr::get(
2286 fn.getContext(),
2287 /*other=*/mlir::LLVM::ModRefInfo::NoModRef,
2288 /*argMem=*/mlir::LLVM::ModRefInfo::NoModRef,
2289 /*inaccessibleMem=*/mlir::LLVM::ModRefInfo::NoModRef,
2290 /*errnoMem=*/mlir::LLVM::ModRefInfo::NoModRef,
2291 /*targetMem0=*/mlir::LLVM::ModRefInfo::NoModRef,
2292 /*targetMem1=*/mlir::LLVM::ModRefInfo::NoModRef));
2293 fn.setNoUnwind(true);
2294 fn.setWillReturn(true);
2295 break;
2296 }
2297 }
2298
2299 if (op->hasAttr(CIRDialect::getNoReturnAttrName()))
2300 fn.setNoreturn(true);
2301
2302 if (std::optional<cir::InlineKind> inlineKind = op.getInlineKind()) {
2303 fn.setNoInline(*inlineKind == cir::InlineKind::NoInline);
2304 fn.setInlineHint(*inlineKind == cir::InlineKind::InlineHint);
2305 fn.setAlwaysInline(*inlineKind == cir::InlineKind::AlwaysInline);
2306 }
2307
2308 if (std::optional<llvm::StringRef> personality = op.getPersonality())
2309 fn.setPersonality(*personality);
2310
2311 fn.setVisibility_(
2312 lowerCIRVisibilityToLLVMVisibility(op.getGlobalVisibility()));
2313
2314 rewriter.inlineRegionBefore(op.getBody(), fn.getBody(), fn.end());
2315 if (failed(rewriter.convertRegionTypes(&fn.getBody(), *typeConverter,
2316 &signatureConversion)))
2317 return mlir::failure();
2318
2319 rewriter.eraseOp(op);
2320
2321 return mlir::LogicalResult::success();
2322}
2323
2324mlir::LogicalResult CIRToLLVMGetGlobalOpLowering::matchAndRewrite(
2325 cir::GetGlobalOp op, OpAdaptor adaptor,
2326 mlir::ConversionPatternRewriter &rewriter) const {
2327 // FIXME(cir): Premature DCE to avoid lowering stuff we're not using.
2328 // CIRGen should mitigate this and not emit the get_global.
2329 if (op->getUses().empty()) {
2330 rewriter.eraseOp(op);
2331 return mlir::success();
2332 }
2333
2334 mlir::Type type = getTypeConverter()->convertType(op.getType());
2335 mlir::Operation *newop = mlir::LLVM::AddressOfOp::create(
2336 rewriter, op.getLoc(), type, op.getName());
2337
2338 if (op.getTls()) {
2339 // Handle access to TLS via intrinsic.
2340 newop = mlir::LLVM::ThreadlocalAddressOp::create(rewriter, op.getLoc(),
2341 type, newop->getResult(0));
2342 }
2343
2344 rewriter.replaceOp(op, newop);
2345 return mlir::success();
2346}
2347
2348llvm::SmallVector<mlir::NamedAttribute>
2349CIRToLLVMGlobalOpLowering::lowerGlobalAttributes(
2350 cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const {
2351 SmallVector<mlir::NamedAttribute> attributes;
2352
2353 if (mlir::StringAttr sectionAttr = op.getSectionAttr())
2354 attributes.push_back(rewriter.getNamedAttr("section", sectionAttr));
2355
2356 mlir::LLVM::VisibilityAttr visibility = mlir::LLVM::VisibilityAttr::get(
2357 getContext(),
2358 lowerCIRVisibilityToLLVMVisibility(op.getGlobalVisibility()));
2359 attributes.push_back(rewriter.getNamedAttr("visibility_", visibility));
2360
2361 if (op->getAttr(CUDAExternallyInitializedAttr::getMnemonic()))
2362 attributes.push_back(rewriter.getNamedAttr("externally_initialized",
2363 rewriter.getUnitAttr()));
2364
2365 return attributes;
2366}
2367
2368/// Replace CIR global with a region initialized LLVM global and update
2369/// insertion point to the end of the initializer block.
2370void CIRToLLVMGlobalOpLowering::setupRegionInitializedLLVMGlobalOp(
2371 cir::GlobalOp op, mlir::ConversionPatternRewriter &rewriter) const {
2372 const mlir::Type llvmType =
2373 convertTypeForMemory(*getTypeConverter(), dataLayout, op.getSymType());
2374
2375 // FIXME: These default values are placeholders until the the equivalent
2376 // attributes are available on cir.global ops. This duplicates code
2377 // in CIRToLLVMGlobalOpLowering::matchAndRewrite() but that will go
2378 // away when the placeholders are no longer needed.
2379 const bool isConst = op.getConstant();
2380 unsigned addrSpace = 0;
2381 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
2382 op.getAddrSpaceAttr()))
2383 addrSpace = targetAS.getValue();
2384 const bool isDsoLocal = op.getDsoLocal();
2385 const bool isThreadLocal = (bool)op.getTlsModelAttr();
2386 const uint64_t alignment = op.getAlignment().value_or(0);
2387 const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
2388 const StringRef symbol = op.getSymName();
2389 mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
2390
2391 SmallVector<mlir::NamedAttribute> attributes =
2392 lowerGlobalAttributes(op, rewriter);
2393
2394 mlir::LLVM::GlobalOp newGlobalOp =
2395 rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
2396 op, llvmType, isConst, linkage, symbol, nullptr, alignment, addrSpace,
2397 isDsoLocal, isThreadLocal, comdatAttr, attributes);
2398 newGlobalOp.getRegion().emplaceBlock();
2399 rewriter.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());
2400}
2401
2402mlir::LogicalResult
2403CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
2404 cir::GlobalOp op, mlir::Attribute init,
2405 mlir::ConversionPatternRewriter &rewriter) const {
2406 // TODO: Generalize this handling when more types are needed here.
2407 assert((isa<cir::ConstArrayAttr, cir::ConstRecordAttr, cir::ConstVectorAttr,
2408 cir::ConstPtrAttr, cir::ConstComplexAttr, cir::GlobalViewAttr,
2409 cir::TypeInfoAttr, cir::UndefAttr, cir::PoisonAttr,
2410 cir::VTableAttr, cir::ZeroAttr>(init)));
2411
2412 // TODO(cir): once LLVM's dialect has proper equivalent attributes this
2413 // should be updated. For now, we use a custom op to initialize globals
2414 // to the appropriate value.
2415 const mlir::Location loc = op.getLoc();
2416 setupRegionInitializedLLVMGlobalOp(op, rewriter);
2417 CIRAttrToValue valueConverter(op, rewriter, typeConverter);
2418 mlir::Value value = valueConverter.visit(init);
2419 mlir::LLVM::ReturnOp::create(rewriter, loc, value);
2420 return mlir::success();
2421}
2422
2423mlir::LogicalResult CIRToLLVMGlobalOpLowering::matchAndRewrite(
2424 cir::GlobalOp op, OpAdaptor adaptor,
2425 mlir::ConversionPatternRewriter &rewriter) const {
2426 // If this global requires non-trivial initialization or destruction,
2427 // that needs to be moved to runtime handlers during LoweringPrepare.
2428 if (!op.getCtorRegion().empty() || !op.getDtorRegion().empty())
2429 return op.emitError() << "GlobalOp ctor and dtor regions should be removed "
2430 "in LoweringPrepare";
2431
2432 std::optional<mlir::Attribute> init = op.getInitialValue();
2433
2434 // Fetch required values to create LLVM op.
2435 const mlir::Type cirSymType = op.getSymType();
2436
2437 // This is the LLVM dialect type.
2438 const mlir::Type llvmType =
2439 convertTypeForMemory(*getTypeConverter(), dataLayout, cirSymType);
2440
2441 // FIXME: These default values are placeholders until the the equivalent
2442 // attributes are available on cir.global ops.
2443 const bool isConst = op.getConstant();
2444 unsigned addrSpace = 0;
2445 if (auto targetAS = mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
2446 op.getAddrSpaceAttr()))
2447 addrSpace = targetAS.getValue();
2448 const bool isDsoLocal = op.getDsoLocal();
2449 const bool isThreadLocal = (bool)op.getTlsModelAttr();
2450 const uint64_t alignment = op.getAlignment().value_or(0);
2451 const mlir::LLVM::Linkage linkage = convertLinkage(op.getLinkage());
2452 const StringRef symbol = op.getSymName();
2453 SmallVector<mlir::NamedAttribute> attributes =
2454 lowerGlobalAttributes(op, rewriter);
2455
2456 // If this is a variable alias, lower it to llvm.mlir.alias.
2457 if (std::optional<llvm::StringRef> aliasee = op.getAliasee()) {
2458 mlir::Location loc = op.getLoc();
2459 auto aliasOp = rewriter.replaceOpWithNewOp<mlir::LLVM::AliasOp>(
2460 op, llvmType, linkage, symbol, isDsoLocal, isThreadLocal, attributes);
2461
2462 mlir::OpBuilder builder(op.getContext());
2463 mlir::Block *block = builder.createBlock(&aliasOp.getInitializerRegion());
2464 builder.setInsertionPointToStart(block);
2465 mlir::Type ptrTy =
2466 mlir::LLVM::LLVMPointerType::get(getContext(), addrSpace);
2467 auto addrOp =
2468 mlir::LLVM::AddressOfOp::create(builder, loc, ptrTy, *aliasee);
2469 mlir::LLVM::ReturnOp::create(builder, loc, addrOp);
2470 return mlir::success();
2471 }
2472
2473 if (init.has_value()) {
2474 if (mlir::isa<cir::FPAttr, cir::IntAttr, cir::BoolAttr>(init.value())) {
2475 GlobalInitAttrRewriter initRewriter(llvmType, rewriter);
2476 init = initRewriter.visit(init.value());
2477 // If initRewriter returned a null attribute, init will have a value but
2478 // the value will be null. If that happens, initRewriter didn't handle the
2479 // attribute type. It probably needs to be added to
2480 // GlobalInitAttrRewriter.
2481 if (!init.value()) {
2482 op.emitError() << "unsupported initializer '" << init.value() << "'";
2483 return mlir::failure();
2484 }
2485 } else if (auto constArr =
2486 mlir::dyn_cast<cir::ConstArrayAttr>(init.value())) {
2487 // Bulk-emit llvm.mlir.global when lowerConstArrayAttr can build the
2488 // whole initializer as one aggregate attribute (no insertvalue
2489 // region). Leaf type must match what lowerConstArrayAttr handles
2490 // (pointers, integers, bools, floats, and string literals with
2491 // trailing_zeros).
2493 getConstArrayBaseElementType(constArr.getType()))) {
2494 mlir::ModuleOp modOp = op->getParentOfType<mlir::ModuleOp>();
2495 if (std::optional<mlir::Attribute> bulkInit =
2496 lowerConstArrayAttr(constArr, typeConverter, modOp)) {
2497 mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
2498 rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
2499 op, llvmType, isConst, linkage, symbol, bulkInit.value(),
2500 alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr,
2501 attributes);
2502 return mlir::success();
2503 }
2504 }
2505 return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
2506 } else if (mlir::isa<cir::ConstVectorAttr, cir::ConstRecordAttr,
2507 cir::ConstPtrAttr, cir::ConstComplexAttr,
2508 cir::GlobalViewAttr, cir::TypeInfoAttr, cir::UndefAttr,
2509 cir::PoisonAttr, cir::VTableAttr, cir::ZeroAttr>(
2510 init.value())) {
2511 // TODO(cir): once LLVM's dialect has proper equivalent attributes this
2512 // should be updated. For now, we use a custom op to initialize globals
2513 // to the appropriate value.
2514 return matchAndRewriteRegionInitializedGlobal(op, init.value(), rewriter);
2515 } else {
2516 // We will only get here if new initializer types are added and this
2517 // code is not updated to handle them.
2518 op.emitError() << "unsupported initializer '" << init.value() << "'";
2519 return mlir::failure();
2520 }
2521 }
2522
2523 mlir::SymbolRefAttr comdatAttr = getComdatAttr(op, rewriter);
2524 rewriter.replaceOpWithNewOp<mlir::LLVM::GlobalOp>(
2525 op, llvmType, isConst, linkage, symbol, init.value_or(mlir::Attribute()),
2526 alignment, addrSpace, isDsoLocal, isThreadLocal, comdatAttr, attributes);
2527
2528 return mlir::success();
2529}
2530
2531mlir::SymbolRefAttr
2532CIRToLLVMGlobalOpLowering::getComdatAttr(cir::GlobalOp &op,
2533 mlir::OpBuilder &builder) const {
2534 if (!op.getComdat())
2535 return mlir::SymbolRefAttr{};
2536
2537 mlir::ModuleOp modOp = op->getParentOfType<mlir::ModuleOp>();
2538 mlir::OpBuilder::InsertionGuard guard(builder);
2539 StringRef comdatName("__llvm_comdat_globals");
2540 if (!comdatOp) {
2541 builder.setInsertionPointToStart(modOp.getBody());
2542 comdatOp =
2543 mlir::LLVM::ComdatOp::create(builder, modOp.getLoc(), comdatName);
2544 }
2545
2546 if (auto comdatSelector = comdatOp.lookupSymbol<mlir::LLVM::ComdatSelectorOp>(
2547 op.getSymName())) {
2548 return mlir::SymbolRefAttr::get(
2549 builder.getContext(), comdatName,
2550 mlir::FlatSymbolRefAttr::get(comdatSelector.getSymNameAttr()));
2551 }
2552
2553 builder.setInsertionPointToStart(&comdatOp.getBody().back());
2554 auto selectorOp = mlir::LLVM::ComdatSelectorOp::create(
2555 builder, comdatOp.getLoc(), op.getSymName(),
2556 mlir::LLVM::comdat::Comdat::Any);
2557 return mlir::SymbolRefAttr::get(
2558 builder.getContext(), comdatName,
2559 mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr()));
2560}
2561
2562mlir::LogicalResult CIRToLLVMSwitchFlatOpLowering::matchAndRewrite(
2563 cir::SwitchFlatOp op, OpAdaptor adaptor,
2564 mlir::ConversionPatternRewriter &rewriter) const {
2565
2566 llvm::SmallVector<mlir::APInt, 8> caseValues;
2567 for (mlir::Attribute val : op.getCaseValues()) {
2568 auto intAttr = cast<cir::IntAttr>(val);
2569 caseValues.push_back(intAttr.getValue());
2570 }
2571
2572 llvm::SmallVector<mlir::Block *, 8> caseDestinations;
2573 llvm::SmallVector<mlir::ValueRange, 8> caseOperands;
2574
2575 for (mlir::Block *x : op.getCaseDestinations())
2576 caseDestinations.push_back(x);
2577
2578 for (mlir::OperandRange x : op.getCaseOperands())
2579 caseOperands.push_back(x);
2580
2581 // Set switch op to branch to the newly created blocks.
2582 rewriter.setInsertionPoint(op);
2583 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
2584 op, adaptor.getCondition(), op.getDefaultDestination(),
2585 op.getDefaultOperands(), caseValues, caseDestinations, caseOperands);
2586 return mlir::success();
2587}
2588
2589static mlir::LLVM::IntegerOverflowFlags nswFlag(bool nsw) {
2590 return nsw ? mlir::LLVM::IntegerOverflowFlags::nsw
2591 : mlir::LLVM::IntegerOverflowFlags::none;
2592}
2593
2594template <typename CIROp, typename LLVMIntOp>
2595static mlir::LogicalResult
2596lowerIncDecOp(CIROp op, typename CIROp::Adaptor adaptor,
2597 mlir::ConversionPatternRewriter &rewriter) {
2598 mlir::Type llvmType = adaptor.getInput().getType();
2599 mlir::Location loc = op.getLoc();
2600
2601 auto maybeNSW = nswFlag(op.getNoSignedWrap());
2602 mlir::LLVM::ConstantOp one;
2603 if (mlir::isa<cir::VectorType>(op.getType())) {
2604 mlir::DenseIntElementsAttr oneVec = mlir::DenseIntElementsAttr::get(
2605 mlir::cast<mlir::ShapedType>(llvmType), 1);
2606 one = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, oneVec);
2607 } else {
2608 one = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, 1);
2609 }
2610 rewriter.replaceOpWithNewOp<LLVMIntOp>(op, adaptor.getInput(), one, maybeNSW);
2611 return mlir::success();
2612}
2613
2614mlir::LogicalResult CIRToLLVMIncOpLowering::matchAndRewrite(
2615 cir::IncOp op, OpAdaptor adaptor,
2616 mlir::ConversionPatternRewriter &rewriter) const {
2617 return lowerIncDecOp<cir::IncOp, mlir::LLVM::AddOp>(op, adaptor, rewriter);
2618}
2619
2620mlir::LogicalResult CIRToLLVMDecOpLowering::matchAndRewrite(
2621 cir::DecOp op, OpAdaptor adaptor,
2622 mlir::ConversionPatternRewriter &rewriter) const {
2623 return lowerIncDecOp<cir::DecOp, mlir::LLVM::SubOp>(op, adaptor, rewriter);
2624}
2625
2626mlir::LogicalResult CIRToLLVMMinusOpLowering::matchAndRewrite(
2627 cir::MinusOp op, OpAdaptor adaptor,
2628 mlir::ConversionPatternRewriter &rewriter) const {
2629 bool isVector = mlir::isa<cir::VectorType>(op.getType());
2630 mlir::Type llvmType = adaptor.getInput().getType();
2631 mlir::Location loc = op.getLoc();
2632
2633 auto maybeNSW = nswFlag(op.getNoSignedWrap());
2634 mlir::Value zero;
2635 if (isVector)
2636 zero = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmType);
2637 else
2638 zero = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, 0);
2639 rewriter.replaceOpWithNewOp<mlir::LLVM::SubOp>(op, zero, adaptor.getInput(),
2640 maybeNSW);
2641 return mlir::success();
2642}
2643
2644mlir::LogicalResult CIRToLLVMNotOpLowering::matchAndRewrite(
2645 cir::NotOp op, OpAdaptor adaptor,
2646 mlir::ConversionPatternRewriter &rewriter) const {
2647 mlir::Type elementType = elementTypeIfVector(op.getType());
2648 bool isVector = mlir::isa<cir::VectorType>(op.getType());
2649 mlir::Type llvmType = adaptor.getInput().getType();
2650 mlir::Location loc = op.getLoc();
2651
2652 if (mlir::isa<cir::IntType>(elementType)) {
2653 mlir::Value minusOne;
2654 if (isVector) {
2655 const uint64_t numElements =
2656 mlir::dyn_cast<cir::VectorType>(op.getType()).getSize();
2657 SmallVector<int32_t> values(numElements, -1);
2658 mlir::DenseIntElementsAttr denseVec = rewriter.getI32VectorAttr(values);
2659 minusOne =
2660 mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, denseVec);
2661 } else {
2662 minusOne = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, -1);
2663 }
2664 rewriter.replaceOpWithNewOp<mlir::LLVM::XOrOp>(op, adaptor.getInput(),
2665 minusOne);
2666 return mlir::success();
2667 }
2668 if (mlir::isa<cir::BoolType>(elementType)) {
2669 auto one = mlir::LLVM::ConstantOp::create(rewriter, loc, llvmType, 1);
2670 rewriter.replaceOpWithNewOp<mlir::LLVM::XOrOp>(op, adaptor.getInput(), one);
2671 return mlir::success();
2672 }
2673 return op.emitError() << "Unsupported type for bitwise NOT";
2674}
2675
2676static bool isIntTypeUnsigned(mlir::Type type) {
2677 // TODO: Ideally, we should only need to check cir::IntType here.
2678 return mlir::isa<cir::IntType>(type)
2679 ? mlir::cast<cir::IntType>(type).isUnsigned()
2680 : mlir::cast<mlir::IntegerType>(type).isUnsigned();
2681}
2682
2683//===----------------------------------------------------------------------===//
2684// Binary Op Lowering
2685//===----------------------------------------------------------------------===//
2686
2687template <typename BinOp>
2688static mlir::LLVM::IntegerOverflowFlags intOverflowFlag(BinOp op) {
2689 if (op.getNoUnsignedWrap())
2690 return mlir::LLVM::IntegerOverflowFlags::nuw;
2691 if (op.getNoSignedWrap())
2692 return mlir::LLVM::IntegerOverflowFlags::nsw;
2693 return mlir::LLVM::IntegerOverflowFlags::none;
2694}
2695
2696/// Lower an arithmetic op that supports saturation, overflow flags, and an FP
2697/// Lower an integer Add/Sub op that may use saturating-arithmetic semantics.
2698template <typename UIntSatOp, typename SIntSatOp, typename IntOp,
2699 typename CIROp>
2700static mlir::LogicalResult
2701lowerSaturatableArithOp(CIROp op, mlir::Value lhs, mlir::Value rhs,
2702 mlir::ConversionPatternRewriter &rewriter) {
2703 const mlir::Type eltType = elementTypeIfVector(op.getRhs().getType());
2704 assert(cir::isIntOrBoolType(eltType) &&
2705 "saturatable arith op expects integer operand types");
2706 if (op.getSaturated()) {
2707 if (isIntTypeUnsigned(eltType))
2708 rewriter.replaceOpWithNewOp<UIntSatOp>(op, lhs, rhs);
2709 else
2710 rewriter.replaceOpWithNewOp<SIntSatOp>(op, lhs, rhs);
2711 return mlir::success();
2712 }
2713 rewriter.replaceOpWithNewOp<IntOp>(op, lhs, rhs, intOverflowFlag(op));
2714 return mlir::success();
2715}
2716
2717mlir::LogicalResult CIRToLLVMAddOpLowering::matchAndRewrite(
2718 cir::AddOp op, OpAdaptor adaptor,
2719 mlir::ConversionPatternRewriter &rewriter) const {
2720 return lowerSaturatableArithOp<mlir::LLVM::UAddSat, mlir::LLVM::SAddSat,
2721 mlir::LLVM::AddOp>(op, adaptor.getLhs(),
2722 adaptor.getRhs(), rewriter);
2723}
2724
2725mlir::LogicalResult CIRToLLVMSubOpLowering::matchAndRewrite(
2726 cir::SubOp op, OpAdaptor adaptor,
2727 mlir::ConversionPatternRewriter &rewriter) const {
2728 return lowerSaturatableArithOp<mlir::LLVM::USubSat, mlir::LLVM::SSubSat,
2729 mlir::LLVM::SubOp>(op, adaptor.getLhs(),
2730 adaptor.getRhs(), rewriter);
2731}
2732
2733mlir::LogicalResult CIRToLLVMMulOpLowering::matchAndRewrite(
2734 cir::MulOp op, OpAdaptor adaptor,
2735 mlir::ConversionPatternRewriter &rewriter) const {
2736 assert(cir::isIntOrBoolType(elementTypeIfVector(op.getRhs().getType())) &&
2737 "cir.mul expects integer operand types");
2738 rewriter.replaceOpWithNewOp<mlir::LLVM::MulOp>(
2739 op, adaptor.getLhs(), adaptor.getRhs(), intOverflowFlag(op));
2740 return mlir::success();
2741}
2742
2743/// Lower an integer Div/Rem op to its signed or unsigned LLVM counterpart.
2744template <typename UIntOp, typename SIntOp, typename CIROp>
2745static mlir::LogicalResult
2746lowerIntBinaryOp(CIROp op, mlir::Value lhs, mlir::Value rhs,
2747 mlir::ConversionPatternRewriter &rewriter) {
2748 const mlir::Type eltType = elementTypeIfVector(op.getRhs().getType());
2749 assert(cir::isIntOrBoolType(eltType) &&
2750 "integer binary op expects integer operand types");
2751 if (isIntTypeUnsigned(eltType))
2752 rewriter.replaceOpWithNewOp<UIntOp>(op, lhs, rhs);
2753 else
2754 rewriter.replaceOpWithNewOp<SIntOp>(op, lhs, rhs);
2755 return mlir::success();
2756}
2757
2758mlir::LogicalResult CIRToLLVMDivOpLowering::matchAndRewrite(
2759 cir::DivOp op, OpAdaptor adaptor,
2760 mlir::ConversionPatternRewriter &rewriter) const {
2762 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
2763}
2764
2765mlir::LogicalResult CIRToLLVMRemOpLowering::matchAndRewrite(
2766 cir::RemOp op, OpAdaptor adaptor,
2767 mlir::ConversionPatternRewriter &rewriter) const {
2769 op, adaptor.getLhs(), adaptor.getRhs(), rewriter);
2770}
2771
2772template <typename CIROp, typename UIntOp, typename SIntOp>
2773static mlir::LogicalResult
2774lowerMinMaxOp(CIROp op, typename CIROp::Adaptor adaptor,
2775 mlir::ConversionPatternRewriter &rewriter) {
2776 const mlir::Value lhs = adaptor.getLhs();
2777 const mlir::Value rhs = adaptor.getRhs();
2778 if (isIntTypeUnsigned(elementTypeIfVector(op.getRhs().getType())))
2779 rewriter.replaceOpWithNewOp<UIntOp>(op, lhs, rhs);
2780 else
2781 rewriter.replaceOpWithNewOp<SIntOp>(op, lhs, rhs);
2782 return mlir::success();
2783}
2784
2785mlir::LogicalResult CIRToLLVMMaxOpLowering::matchAndRewrite(
2786 cir::MaxOp op, OpAdaptor adaptor,
2787 mlir::ConversionPatternRewriter &rewriter) const {
2789 op, adaptor, rewriter);
2790}
2791
2792mlir::LogicalResult CIRToLLVMMinOpLowering::matchAndRewrite(
2793 cir::MinOp op, OpAdaptor adaptor,
2794 mlir::ConversionPatternRewriter &rewriter) const {
2796 op, adaptor, rewriter);
2797}
2798
2799/// Convert from a CIR comparison kind to an LLVM IR integral comparison kind.
2800static mlir::LLVM::ICmpPredicate
2801convertCmpKindToICmpPredicate(cir::CmpOpKind kind, bool isSigned) {
2802 using CIR = cir::CmpOpKind;
2803 using LLVMICmp = mlir::LLVM::ICmpPredicate;
2804 switch (kind) {
2805 case CIR::eq:
2806 return LLVMICmp::eq;
2807 case CIR::ne:
2808 return LLVMICmp::ne;
2809 case CIR::lt:
2810 return (isSigned ? LLVMICmp::slt : LLVMICmp::ult);
2811 case CIR::le:
2812 return (isSigned ? LLVMICmp::sle : LLVMICmp::ule);
2813 case CIR::gt:
2814 return (isSigned ? LLVMICmp::sgt : LLVMICmp::ugt);
2815 case CIR::ge:
2816 return (isSigned ? LLVMICmp::sge : LLVMICmp::uge);
2817 case CIR::one:
2818 case CIR::uno:
2819 llvm_unreachable("FP-only comparison used with integer type");
2820 }
2821 llvm_unreachable("Unknown CmpOpKind");
2822}
2823
2824/// Convert from a CIR comparison kind to an LLVM IR floating-point comparison
2825/// kind.
2826static mlir::LLVM::FCmpPredicate
2828 using CIR = cir::CmpOpKind;
2829 using LLVMFCmp = mlir::LLVM::FCmpPredicate;
2830 switch (kind) {
2831 case CIR::eq:
2832 return LLVMFCmp::oeq;
2833 case CIR::ne:
2834 return LLVMFCmp::une;
2835 case CIR::lt:
2836 return LLVMFCmp::olt;
2837 case CIR::le:
2838 return LLVMFCmp::ole;
2839 case CIR::gt:
2840 return LLVMFCmp::ogt;
2841 case CIR::ge:
2842 return LLVMFCmp::oge;
2843 case CIR::one:
2844 return LLVMFCmp::one;
2845 case CIR::uno:
2846 return LLVMFCmp::uno;
2847 }
2848 llvm_unreachable("Unknown CmpOpKind");
2849}
2850
2851mlir::LogicalResult CIRToLLVMCmpOpLowering::matchAndRewrite(
2852 cir::CmpOp cmpOp, OpAdaptor adaptor,
2853 mlir::ConversionPatternRewriter &rewriter) const {
2854 mlir::Type type = cmpOp.getLhs().getType();
2855
2856 if (mlir::isa<cir::IntType, mlir::IntegerType>(type)) {
2857 bool isSigned = mlir::isa<cir::IntType>(type)
2858 ? mlir::cast<cir::IntType>(type).isSigned()
2859 : mlir::cast<mlir::IntegerType>(type).isSigned();
2860 mlir::LLVM::ICmpPredicate kind =
2861 convertCmpKindToICmpPredicate(cmpOp.getKind(), isSigned);
2862 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
2863 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
2864 return mlir::success();
2865 }
2866
2867 if (auto ptrTy = mlir::dyn_cast<cir::PointerType>(type)) {
2868 mlir::LLVM::ICmpPredicate kind =
2869 convertCmpKindToICmpPredicate(cmpOp.getKind(),
2870 /* isSigned=*/false);
2871 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
2872 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
2873 return mlir::success();
2874 }
2875
2876 if (auto vptrTy = mlir::dyn_cast<cir::VPtrType>(type)) {
2877 // !cir.vptr is a special case, but it's just a pointer to LLVM.
2878 auto kind = convertCmpKindToICmpPredicate(cmpOp.getKind(),
2879 /* isSigned=*/false);
2880 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>(
2881 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
2882 return mlir::success();
2883 }
2884
2885 if (mlir::isa<cir::FPTypeInterface>(type)) {
2886 mlir::LLVM::FCmpPredicate kind =
2887 convertCmpKindToFCmpPredicate(cmpOp.getKind());
2888 rewriter.replaceOpWithNewOp<mlir::LLVM::FCmpOp>(
2889 cmpOp, kind, adaptor.getLhs(), adaptor.getRhs());
2890 return mlir::success();
2891 }
2892
2893 if (mlir::isa<cir::ComplexType>(type)) {
2894 mlir::Value lhs = adaptor.getLhs();
2895 mlir::Value rhs = adaptor.getRhs();
2896 mlir::Location loc = cmpOp.getLoc();
2897
2898 auto complexType = mlir::cast<cir::ComplexType>(cmpOp.getLhs().getType());
2899 mlir::Type complexElemTy =
2900 getTypeConverter()->convertType(complexType.getElementType());
2901
2902 auto lhsReal = mlir::LLVM::ExtractValueOp::create(
2903 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{0}));
2904 auto lhsImag = mlir::LLVM::ExtractValueOp::create(
2905 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{1}));
2906 auto rhsReal = mlir::LLVM::ExtractValueOp::create(
2907 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{0}));
2908 auto rhsImag = mlir::LLVM::ExtractValueOp::create(
2909 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{1}));
2910
2911 if (cmpOp.getKind() == cir::CmpOpKind::eq) {
2912 if (complexElemTy.isInteger()) {
2913 auto realCmp = mlir::LLVM::ICmpOp::create(
2914 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, lhsReal, rhsReal);
2915 auto imagCmp = mlir::LLVM::ICmpOp::create(
2916 rewriter, loc, mlir::LLVM::ICmpPredicate::eq, lhsImag, rhsImag);
2917 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmpOp, realCmp, imagCmp);
2918 return mlir::success();
2919 }
2920
2921 auto realCmp = mlir::LLVM::FCmpOp::create(
2922 rewriter, loc, mlir::LLVM::FCmpPredicate::oeq, lhsReal, rhsReal);
2923 auto imagCmp = mlir::LLVM::FCmpOp::create(
2924 rewriter, loc, mlir::LLVM::FCmpPredicate::oeq, lhsImag, rhsImag);
2925 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmpOp, realCmp, imagCmp);
2926 return mlir::success();
2927 }
2928
2929 if (cmpOp.getKind() == cir::CmpOpKind::ne) {
2930 if (complexElemTy.isInteger()) {
2931 auto realCmp = mlir::LLVM::ICmpOp::create(
2932 rewriter, loc, mlir::LLVM::ICmpPredicate::ne, lhsReal, rhsReal);
2933 auto imagCmp = mlir::LLVM::ICmpOp::create(
2934 rewriter, loc, mlir::LLVM::ICmpPredicate::ne, lhsImag, rhsImag);
2935 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmpOp, realCmp, imagCmp);
2936 return mlir::success();
2937 }
2938
2939 auto realCmp = mlir::LLVM::FCmpOp::create(
2940 rewriter, loc, mlir::LLVM::FCmpPredicate::une, lhsReal, rhsReal);
2941 auto imagCmp = mlir::LLVM::FCmpOp::create(
2942 rewriter, loc, mlir::LLVM::FCmpPredicate::une, lhsImag, rhsImag);
2943 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmpOp, realCmp, imagCmp);
2944 return mlir::success();
2945 }
2946 }
2947
2948 return cmpOp.emitError() << "unsupported type for CmpOp: " << type;
2949}
2950
2951/// Shared lowering logic for checked binary arithmetic overflow operations.
2952/// The \p opStr parameter specifies the arithmetic operation name used in the
2953/// LLVM intrinsic (e.g., "add", "sub", "mul").
2954template <typename OpTy>
2955static mlir::LogicalResult
2956lowerBinOpOverflow(OpTy op, typename OpTy::Adaptor adaptor,
2957 mlir::ConversionPatternRewriter &rewriter,
2958 const mlir::TypeConverter *typeConverter,
2959 llvm::StringRef opStr) {
2960 mlir::Location loc = op.getLoc();
2961 cir::IntType operandTy = op.getLhs().getType();
2962 cir::IntType resultTy = op.getResult().getType();
2963
2964 bool sign = operandTy.getIsSigned() || resultTy.getIsSigned();
2965 unsigned width =
2966 std::max(operandTy.getWidth() + (sign && operandTy.isUnsigned()),
2967 resultTy.getWidth() + (sign && resultTy.isUnsigned()));
2968
2969 mlir::IntegerType encompassedLLVMTy = rewriter.getIntegerType(width);
2970
2971 mlir::Value lhs = adaptor.getLhs();
2972 mlir::Value rhs = adaptor.getRhs();
2973 if (operandTy.getWidth() < width) {
2974 if (operandTy.isSigned()) {
2975 lhs = mlir::LLVM::SExtOp::create(rewriter, loc, encompassedLLVMTy, lhs);
2976 rhs = mlir::LLVM::SExtOp::create(rewriter, loc, encompassedLLVMTy, rhs);
2977 } else {
2978 lhs = mlir::LLVM::ZExtOp::create(rewriter, loc, encompassedLLVMTy, lhs);
2979 rhs = mlir::LLVM::ZExtOp::create(rewriter, loc, encompassedLLVMTy, rhs);
2980 }
2981 }
2982
2983 // The intrinsic name is `@llvm.{s|u}{op}.with.overflow.i{width}`
2984 std::string intrinName = ("llvm." + llvm::Twine(sign ? 's' : 'u') + opStr +
2985 ".with.overflow.i" + llvm::Twine(width))
2986 .str();
2987 auto intrinNameAttr = mlir::StringAttr::get(op.getContext(), intrinName);
2988
2989 mlir::IntegerType overflowLLVMTy = rewriter.getI1Type();
2990 auto intrinRetTy = mlir::LLVM::LLVMStructType::getLiteral(
2991 rewriter.getContext(), {encompassedLLVMTy, overflowLLVMTy});
2992
2993 auto callLLVMIntrinOp = mlir::LLVM::CallIntrinsicOp::create(
2994 rewriter, loc, intrinRetTy, intrinNameAttr, mlir::ValueRange{lhs, rhs});
2995 mlir::Value intrinRet = callLLVMIntrinOp.getResult(0);
2996
2997 mlir::Value result = mlir::LLVM::ExtractValueOp::create(
2998 rewriter, loc, intrinRet, ArrayRef<int64_t>{0})
2999 .getResult();
3000 mlir::Value overflow = mlir::LLVM::ExtractValueOp::create(
3001 rewriter, loc, intrinRet, ArrayRef<int64_t>{1})
3002 .getResult();
3003
3004 if (resultTy.getWidth() < width) {
3005 mlir::Type resultLLVMTy = typeConverter->convertType(resultTy);
3006 auto truncResult =
3007 mlir::LLVM::TruncOp::create(rewriter, loc, resultLLVMTy, result);
3008
3009 // Extend the truncated result back to the encompassing type to check for
3010 // any overflows during the truncation.
3011 mlir::Value truncResultExt;
3012 if (resultTy.isSigned())
3013 truncResultExt = mlir::LLVM::SExtOp::create(
3014 rewriter, loc, encompassedLLVMTy, truncResult);
3015 else
3016 truncResultExt = mlir::LLVM::ZExtOp::create(
3017 rewriter, loc, encompassedLLVMTy, truncResult);
3018 auto truncOverflow = mlir::LLVM::ICmpOp::create(
3019 rewriter, loc, mlir::LLVM::ICmpPredicate::ne, truncResultExt, result);
3020
3021 result = truncResult;
3022 overflow = mlir::LLVM::OrOp::create(rewriter, loc, overflow, truncOverflow);
3023 }
3024
3025 mlir::Type boolLLVMTy =
3026 typeConverter->convertType(op.getOverflow().getType());
3027 if (boolLLVMTy != rewriter.getI1Type())
3028 overflow = mlir::LLVM::ZExtOp::create(rewriter, loc, boolLLVMTy, overflow);
3029
3030 rewriter.replaceOp(op, mlir::ValueRange{result, overflow});
3031
3032 return mlir::success();
3033}
3034
3035mlir::LogicalResult CIRToLLVMAddOverflowOpLowering::matchAndRewrite(
3036 cir::AddOverflowOp op, OpAdaptor adaptor,
3037 mlir::ConversionPatternRewriter &rewriter) const {
3038 return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "add");
3039}
3040
3041mlir::LogicalResult CIRToLLVMSubOverflowOpLowering::matchAndRewrite(
3042 cir::SubOverflowOp op, OpAdaptor adaptor,
3043 mlir::ConversionPatternRewriter &rewriter) const {
3044 return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "sub");
3045}
3046
3047mlir::LogicalResult CIRToLLVMMulOverflowOpLowering::matchAndRewrite(
3048 cir::MulOverflowOp op, OpAdaptor adaptor,
3049 mlir::ConversionPatternRewriter &rewriter) const {
3050 return lowerBinOpOverflow(op, adaptor, rewriter, getTypeConverter(), "mul");
3051}
3052
3053mlir::LogicalResult CIRToLLVMFrexpOpLowering::matchAndRewrite(
3054 cir::FrexpOp op, OpAdaptor adaptor,
3055 mlir::ConversionPatternRewriter &rewriter) const {
3056 mlir::Location loc = op.getLoc();
3057 mlir::Type fpLLVMTy =
3058 getTypeConverter()->convertType(op.getResult().getType());
3059 mlir::Type intLLVMTy = getTypeConverter()->convertType(op.getExp().getType());
3060
3061 auto structTy = mlir::LLVM::LLVMStructType::getLiteral(rewriter.getContext(),
3062 {fpLLVMTy, intLLVMTy});
3063
3064 auto callOp = createCallLLVMIntrinsicOp(rewriter, loc, "llvm.frexp", structTy,
3065 adaptor.getSrc());
3066 mlir::Value result = callOp.getResult(0);
3067
3068 mlir::Value mantissa =
3069 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 0);
3070 mlir::Value exponent =
3071 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 1);
3072 rewriter.replaceOp(op, mlir::ValueRange{mantissa, exponent});
3073 return mlir::success();
3074}
3075
3076mlir::LogicalResult CIRToLLVMModfOpLowering::matchAndRewrite(
3077 cir::ModfOp op, OpAdaptor adaptor,
3078 mlir::ConversionPatternRewriter &rewriter) const {
3079 mlir::Location loc = op.getLoc();
3080 mlir::Type fpLLVMTy =
3081 getTypeConverter()->convertType(op.getFractional().getType());
3082
3083 auto structTy = mlir::LLVM::LLVMStructType::getLiteral(rewriter.getContext(),
3084 {fpLLVMTy, fpLLVMTy});
3085
3086 auto callOp = createCallLLVMIntrinsicOp(rewriter, loc, "llvm.modf", structTy,
3087 adaptor.getSrc());
3088 mlir::Value result = callOp.getResult(0);
3089
3090 mlir::Value fractional =
3091 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 0);
3092 mlir::Value integral =
3093 mlir::LLVM::ExtractValueOp::create(rewriter, loc, result, 1);
3094 rewriter.replaceOp(op, mlir::ValueRange{fractional, integral});
3095 return mlir::success();
3096}
3097
3098mlir::LogicalResult CIRToLLVMShiftOpLowering::matchAndRewrite(
3099 cir::ShiftOp op, OpAdaptor adaptor,
3100 mlir::ConversionPatternRewriter &rewriter) const {
3101 assert((op.getValue().getType() == op.getType()) &&
3102 "inconsistent operands' types NYI");
3103
3104 const mlir::Type llvmTy = getTypeConverter()->convertType(op.getType());
3105 mlir::Value amt = adaptor.getAmount();
3106 mlir::Value val = adaptor.getValue();
3107
3108 auto cirAmtTy = mlir::dyn_cast<cir::IntType>(op.getAmount().getType());
3109 bool isUnsigned;
3110 if (cirAmtTy) {
3111 auto cirValTy = mlir::cast<cir::IntType>(op.getValue().getType());
3112 isUnsigned = cirValTy.isUnsigned();
3113
3114 // Ensure shift amount is the same type as the value. Some undefined
3115 // behavior might occur in the casts below as per [C99 6.5.7.3].
3116 // Vector type shift amount needs no cast as type consistency is expected to
3117 // be already be enforced at CIRGen.
3118 if (cirAmtTy)
3119 amt = getLLVMIntCast(rewriter, amt, llvmTy, true, cirAmtTy.getWidth(),
3120 cirValTy.getWidth());
3121 } else {
3122 auto cirValVTy = mlir::cast<cir::VectorType>(op.getValue().getType());
3123 isUnsigned =
3124 mlir::cast<cir::IntType>(cirValVTy.getElementType()).isUnsigned();
3125 }
3126
3127 // Lower to the proper LLVM shift operation.
3128 if (op.getIsShiftleft()) {
3129 rewriter.replaceOpWithNewOp<mlir::LLVM::ShlOp>(op, llvmTy, val, amt);
3130 return mlir::success();
3131 }
3132
3133 if (isUnsigned)
3134 rewriter.replaceOpWithNewOp<mlir::LLVM::LShrOp>(op, llvmTy, val, amt);
3135 else
3136 rewriter.replaceOpWithNewOp<mlir::LLVM::AShrOp>(op, llvmTy, val, amt);
3137 return mlir::success();
3138}
3139
3140mlir::LogicalResult CIRToLLVMSelectOpLowering::matchAndRewrite(
3141 cir::SelectOp op, OpAdaptor adaptor,
3142 mlir::ConversionPatternRewriter &rewriter) const {
3143 auto getConstantBool = [](mlir::Value value) -> cir::BoolAttr {
3144 auto definingOp = value.getDefiningOp<cir::ConstantOp>();
3145 if (!definingOp)
3146 return {};
3147
3148 auto constValue = definingOp.getValueAttr<cir::BoolAttr>();
3149 if (!constValue)
3150 return {};
3151
3152 return constValue;
3153 };
3154
3155 // Two special cases in the LLVMIR codegen of select op:
3156 // - select %0, %1, false => and %0, %1
3157 // - select %0, true, %1 => or %0, %1
3158 if (mlir::isa<cir::BoolType>(op.getTrueValue().getType())) {
3159 cir::BoolAttr trueValue = getConstantBool(op.getTrueValue());
3160 cir::BoolAttr falseValue = getConstantBool(op.getFalseValue());
3161 if (falseValue && !falseValue.getValue()) {
3162 // select %0, %1, false => and %0, %1
3163 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(op, adaptor.getCondition(),
3164 adaptor.getTrueValue());
3165 return mlir::success();
3166 }
3167 if (trueValue && trueValue.getValue()) {
3168 // select %0, true, %1 => or %0, %1
3169 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(op, adaptor.getCondition(),
3170 adaptor.getFalseValue());
3171 return mlir::success();
3172 }
3173 }
3174
3175 mlir::Value llvmCondition = adaptor.getCondition();
3176 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(
3177 op, llvmCondition, adaptor.getTrueValue(), adaptor.getFalseValue());
3178
3179 return mlir::success();
3180}
3181
3182static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
3183 mlir::DataLayout &dataLayout) {
3184 converter.addConversion([&](cir::PointerType type) -> mlir::Type {
3185 mlir::ptr::MemorySpaceAttrInterface addrSpaceAttr = type.getAddrSpace();
3186 unsigned numericAS = 0;
3187
3188 if (auto targetAsAttr =
3189 mlir::dyn_cast_if_present<cir::TargetAddressSpaceAttr>(
3190 addrSpaceAttr))
3191 numericAS = targetAsAttr.getValue();
3192 return mlir::LLVM::LLVMPointerType::get(type.getContext(), numericAS);
3193 });
3194 converter.addConversion([&](cir::VPtrType type) -> mlir::Type {
3196 return mlir::LLVM::LLVMPointerType::get(type.getContext());
3197 });
3198 converter.addConversion([&](cir::ArrayType type) -> mlir::Type {
3199 mlir::Type ty =
3200 convertTypeForMemory(converter, dataLayout, type.getElementType());
3201 return mlir::LLVM::LLVMArrayType::get(ty, type.getSize());
3202 });
3203 converter.addConversion([&](cir::VectorType type) -> mlir::Type {
3204 const mlir::Type ty = converter.convertType(type.getElementType());
3205 return mlir::VectorType::get(type.getSize(), ty, {type.getIsScalable()});
3206 });
3207 converter.addConversion([&](cir::BoolType type) -> mlir::Type {
3208 return mlir::IntegerType::get(type.getContext(), 1,
3209 mlir::IntegerType::Signless);
3210 });
3211 converter.addConversion([&](cir::IntType type) -> mlir::Type {
3212 // LLVM doesn't work with signed types, so we drop the CIR signs here.
3213 return mlir::IntegerType::get(type.getContext(), type.getWidth());
3214 });
3215 converter.addConversion([&](cir::SingleType type) -> mlir::Type {
3216 return mlir::Float32Type::get(type.getContext());
3217 });
3218 converter.addConversion([&](cir::DoubleType type) -> mlir::Type {
3219 return mlir::Float64Type::get(type.getContext());
3220 });
3221 converter.addConversion([&](cir::FP80Type type) -> mlir::Type {
3222 return mlir::Float80Type::get(type.getContext());
3223 });
3224 converter.addConversion([&](cir::FP128Type type) -> mlir::Type {
3225 return mlir::Float128Type::get(type.getContext());
3226 });
3227 converter.addConversion([&](cir::LongDoubleType type) -> mlir::Type {
3228 return converter.convertType(type.getUnderlying());
3229 });
3230 converter.addConversion([&](cir::FP16Type type) -> mlir::Type {
3231 return mlir::Float16Type::get(type.getContext());
3232 });
3233 converter.addConversion([&](cir::BF16Type type) -> mlir::Type {
3234 return mlir::BFloat16Type::get(type.getContext());
3235 });
3236 converter.addConversion([&](cir::ComplexType type) -> mlir::Type {
3237 // A complex type is lowered to an LLVM struct that contains the real and
3238 // imaginary part as data fields.
3239 mlir::Type elementTy = converter.convertType(type.getElementType());
3240 mlir::Type structFields[2] = {elementTy, elementTy};
3241 return mlir::LLVM::LLVMStructType::getLiteral(type.getContext(),
3242 structFields);
3243 });
3244 converter.addConversion([&](cir::FuncType type) -> std::optional<mlir::Type> {
3245 auto result = converter.convertType(type.getReturnType());
3247 arguments.reserve(type.getNumInputs());
3248 if (converter.convertTypes(type.getInputs(), arguments).failed())
3249 return std::nullopt;
3250 auto varArg = type.isVarArg();
3251 return mlir::LLVM::LLVMFunctionType::get(result, arguments, varArg);
3252 });
3253 converter.addConversion([&](cir::StructType type) -> mlir::Type {
3255 for (mlir::Type ty : type.getMembers())
3256 llvmMembers.push_back(convertTypeForMemory(converter, dataLayout, ty));
3257
3258 mlir::LLVM::LLVMStructType llvmStruct;
3259 if (type.getName()) {
3260 llvmStruct = mlir::LLVM::LLVMStructType::getIdentified(
3261 type.getContext(), type.getPrefixedName());
3262 if (llvmStruct.setBody(llvmMembers, type.getPacked()).failed())
3263 llvm_unreachable("Failed to set body of record");
3264 } else {
3265 llvmStruct = mlir::LLVM::LLVMStructType::getLiteral(
3266 type.getContext(), llvmMembers, type.getPacked());
3267 }
3268 return llvmStruct;
3269 });
3270 // Unions are lowered as only the largest member.
3271 converter.addConversion([&](cir::UnionType type) -> mlir::Type {
3273 if (!type.getMembers().empty())
3274 if (auto storage = type.getUnionStorageType(dataLayout))
3275 llvmMembers.push_back(
3276 convertTypeForMemory(converter, dataLayout, storage));
3277 if (mlir::Type pad = type.getPadding())
3278 llvmMembers.push_back(convertTypeForMemory(converter, dataLayout, pad));
3279
3280 mlir::LLVM::LLVMStructType llvmStruct;
3281 if (type.getName()) {
3282 llvmStruct = mlir::LLVM::LLVMStructType::getIdentified(
3283 type.getContext(), type.getPrefixedName());
3284 if (llvmStruct.setBody(llvmMembers, type.getPacked()).failed())
3285 llvm_unreachable("Failed to set body of record");
3286 } else {
3287 llvmStruct = mlir::LLVM::LLVMStructType::getLiteral(
3288 type.getContext(), llvmMembers, type.getPacked());
3289 }
3290 return llvmStruct;
3291 });
3292 converter.addConversion([&](cir::VoidType type) -> mlir::Type {
3293 return mlir::LLVM::LLVMVoidType::get(type.getContext());
3294 });
3295}
3296
3298 mlir::ModuleOp module, StringRef globalXtorName, StringRef llvmXtorName,
3299 llvm::function_ref<std::pair<StringRef, int>(mlir::Attribute)> createXtor) {
3301 for (const mlir::NamedAttribute namedAttr : module->getAttrs()) {
3302 if (namedAttr.getName() == globalXtorName) {
3303 for (auto attr : mlir::cast<mlir::ArrayAttr>(namedAttr.getValue()))
3304 globalXtors.emplace_back(createXtor(attr));
3305 break;
3306 }
3307 }
3308
3309 if (globalXtors.empty())
3310 return;
3311
3312 mlir::OpBuilder builder(module.getContext());
3313 builder.setInsertionPointToEnd(&module.getBodyRegion().back());
3314
3315 // Create a global array llvm.global_ctors with element type of
3316 // struct { i32, ptr, ptr }
3317 auto ctorPFTy = mlir::LLVM::LLVMPointerType::get(builder.getContext());
3318 llvm::SmallVector<mlir::Type> ctorStructFields;
3319 ctorStructFields.push_back(builder.getI32Type());
3320 ctorStructFields.push_back(ctorPFTy);
3321 ctorStructFields.push_back(ctorPFTy);
3322
3323 auto ctorStructTy = mlir::LLVM::LLVMStructType::getLiteral(
3324 builder.getContext(), ctorStructFields);
3325 auto ctorStructArrayTy =
3326 mlir::LLVM::LLVMArrayType::get(ctorStructTy, globalXtors.size());
3327
3328 mlir::Location loc = module.getLoc();
3329 auto newGlobalOp = mlir::LLVM::GlobalOp::create(
3330 builder, loc, ctorStructArrayTy, /*constant=*/false,
3331 mlir::LLVM::Linkage::Appending, llvmXtorName, mlir::Attribute());
3332
3333 builder.createBlock(&newGlobalOp.getRegion());
3334 builder.setInsertionPointToEnd(newGlobalOp.getInitializerBlock());
3335
3336 mlir::Value result =
3337 mlir::LLVM::UndefOp::create(builder, loc, ctorStructArrayTy);
3338
3339 for (auto [index, fn] : llvm::enumerate(globalXtors)) {
3340 mlir::Value structInit =
3341 mlir::LLVM::UndefOp::create(builder, loc, ctorStructTy);
3342 mlir::Value initPriority = mlir::LLVM::ConstantOp::create(
3343 builder, loc, ctorStructFields[0], fn.second);
3344 mlir::Value initFuncAddr = mlir::LLVM::AddressOfOp::create(
3345 builder, loc, ctorStructFields[1], fn.first);
3346 mlir::Value initAssociate =
3347 mlir::LLVM::ZeroOp::create(builder, loc, ctorStructFields[2]);
3348 // Literal zero makes the InsertValueOp::create ambiguous.
3350 structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
3351 initPriority, zero);
3352 structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
3353 initFuncAddr, 1);
3354 // TODO: handle associated data for initializers.
3355 structInit = mlir::LLVM::InsertValueOp::create(builder, loc, structInit,
3356 initAssociate, 2);
3357 result = mlir::LLVM::InsertValueOp::create(builder, loc, result, structInit,
3358 index);
3359 }
3360
3361 mlir::LLVM::ReturnOp::create(builder, loc, result);
3362}
3363
3364mlir::LogicalResult CIRToLLVMObjSizeOpLowering::matchAndRewrite(
3365 cir::ObjSizeOp op, OpAdaptor adaptor,
3366 mlir::ConversionPatternRewriter &rewriter) const {
3367 mlir::Type llvmResTy = getTypeConverter()->convertType(op.getType());
3368 mlir::Location loc = op->getLoc();
3369
3370 mlir::IntegerType i1Ty = rewriter.getI1Type();
3371
3372 auto i1Val = [&rewriter, &loc, &i1Ty](bool val) {
3373 return mlir::LLVM::ConstantOp::create(rewriter, loc, i1Ty, val);
3374 };
3375
3376 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.objectsize", llvmResTy,
3377 {
3378 adaptor.getPtr(),
3379 i1Val(op.getMin()),
3380 i1Val(op.getNullunknown()),
3381 i1Val(op.getDynamic()),
3382 });
3383
3384 return mlir::LogicalResult::success();
3385}
3386
3387//===----------------------------------------------------------------------===//
3388// @llvm.global.annotations emission
3389//===----------------------------------------------------------------------===//
3390
3391namespace {
3392constexpr StringRef llvmMetadataSectionName = "llvm.metadata";
3393
3394/// Get-or-create a private constant string global in the llvm.metadata
3395/// section, deduplicated by string content.
3396mlir::LLVM::GlobalOp
3397getOrCreateAnnotationStringGlobal(mlir::OpBuilder &builder, mlir::Location loc,
3398 mlir::ModuleOp module, llvm::StringRef str,
3399 llvm::StringMap<mlir::LLVM::GlobalOp> &cache,
3400 bool isArg) {
3401 auto it = cache.find(str);
3402 if (it != cache.end())
3403 return it->second;
3404
3405 auto i8Ty = mlir::IntegerType::get(module.getContext(), 8);
3406 auto arrayTy = mlir::LLVM::LLVMArrayType::get(i8Ty, str.size() + 1);
3407 std::string name = ".str";
3408 if (!cache.empty())
3409 name += "." + std::to_string(cache.size());
3410 name += ".annotation";
3411 if (isArg)
3412 name += ".arg";
3413
3414 mlir::LLVM::GlobalOp strGlobal = mlir::LLVM::GlobalOp::create(
3415 builder, loc, arrayTy, /*isConstant=*/true, mlir::LLVM::Linkage::Private,
3416 name, mlir::StringAttr::get(module.getContext(), std::string(str) + '\0'),
3417 /*alignment=*/isArg ? 1 : 0);
3418 if (!isArg)
3419 strGlobal.setSection(llvmMetadataSectionName);
3420 strGlobal.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Global);
3421 strGlobal.setDsoLocal(true);
3422 cache[str] = strGlobal;
3423 return strGlobal;
3424}
3425
3426/// Get-or-create a private constant struct holding the annotation arguments,
3427/// deduplicated by ArrayAttr identity.
3428mlir::LLVM::GlobalOp getOrCreateAnnotationArgsVar(
3429 mlir::OpBuilder &builder, mlir::Location loc, mlir::ModuleOp module,
3430 mlir::ArrayAttr argsAttr,
3431 llvm::StringMap<mlir::LLVM::GlobalOp> &argStringCache,
3432 llvm::MapVector<mlir::ArrayAttr, mlir::LLVM::GlobalOp> &argsCache) {
3433 auto it = argsCache.find(argsAttr);
3434 if (it != argsCache.end())
3435 return it->second;
3436
3437 auto ptrTy = mlir::LLVM::LLVMPointerType::get(builder.getContext());
3438
3439 llvm::SmallVector<mlir::Type> fieldTypes;
3440 for (mlir::Attribute arg : argsAttr) {
3441 if (mlir::isa<mlir::StringAttr>(arg))
3442 fieldTypes.push_back(ptrTy);
3443 else if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(arg))
3444 fieldTypes.push_back(intAttr.getType());
3445 else
3446 llvm_unreachable("Unsupported annotation arg type");
3447 }
3448
3449 auto structTy =
3450 mlir::LLVM::LLVMStructType::getLiteral(builder.getContext(), fieldTypes);
3451 std::string name = ".args";
3452 if (!argsCache.empty())
3453 name += "." + std::to_string(argsCache.size());
3454 name += ".annotation";
3455
3456 mlir::LLVM::GlobalOp argsGlobal = mlir::LLVM::GlobalOp::create(
3457 builder, loc, structTy, /*isConstant=*/true, mlir::LLVM::Linkage::Private,
3458 name, mlir::Attribute());
3459 argsGlobal.setSection(llvmMetadataSectionName);
3460 argsGlobal.setUnnamedAddr(mlir::LLVM::UnnamedAddr::Global);
3461 argsGlobal.setDsoLocal(true);
3462
3463 // Build the initializer block.
3464 argsGlobal.getRegion().push_back(new mlir::Block());
3465 mlir::OpBuilder initBuilder(module.getContext());
3466 initBuilder.setInsertionPointToEnd(argsGlobal.getInitializerBlock());
3467
3468 mlir::Value structInit =
3469 mlir::LLVM::UndefOp::create(initBuilder, loc, structTy);
3470 for (auto [idx, arg] : llvm::enumerate(argsAttr)) {
3471 if (auto strArg = mlir::dyn_cast<mlir::StringAttr>(arg)) {
3472 mlir::LLVM::GlobalOp strGlobal = getOrCreateAnnotationStringGlobal(
3473 builder, loc, module, strArg.getValue(), argStringCache,
3474 /*isArg=*/true);
3475 mlir::LLVM::AddressOfOp strAddr = mlir::LLVM::AddressOfOp::create(
3476 initBuilder, loc, ptrTy, strGlobal.getSymName());
3477 structInit = mlir::LLVM::InsertValueOp::create(initBuilder, loc,
3478 structInit, strAddr, idx);
3479 } else if (auto intArg = mlir::dyn_cast<mlir::IntegerAttr>(arg)) {
3480 mlir::LLVM::ConstantOp intConst = mlir::LLVM::ConstantOp::create(
3481 initBuilder, loc, intArg.getType(), intArg.getValue());
3482 structInit = mlir::LLVM::InsertValueOp::create(initBuilder, loc,
3483 structInit, intConst, idx);
3484 } else {
3485 llvm_unreachable("Unsupported annotation arg type");
3486 }
3487 }
3488 mlir::LLVM::ReturnOp::create(initBuilder, loc, structInit);
3489
3490 argsCache[argsAttr] = argsGlobal;
3491 return argsGlobal;
3492}
3493
3494/// Resolve a possibly-fused MLIR Location to a FileLineColLoc, returning
3495/// {filename, line}. Returns {empty, 0} if no usable file location is found.
3496std::pair<llvm::StringRef, unsigned> extractFileLine(mlir::Location loc) {
3497 mlir::Location resolved = loc;
3498 if (auto fused = mlir::dyn_cast<mlir::FusedLoc>(resolved)) {
3499 if (!fused.getLocations().empty())
3500 resolved = fused.getLocations()[0];
3501 }
3502 if (auto fl = mlir::dyn_cast<mlir::FileLineColLoc>(resolved))
3503 return {fl.getFilename().getValue(), fl.getLine()};
3504 return {"", 0};
3505}
3506} // namespace
3507
3509 auto handleArray = [&](mlir::StringAttr symName, mlir::ArrayAttr arr,
3510 mlir::Location loc) {
3511 if (!arr)
3512 return;
3513 for (mlir::Attribute a : arr)
3514 if (auto annot = mlir::dyn_cast<cir::AnnotationAttr>(a))
3515 collectedAnnotations.emplace_back(symName, annot, loc);
3516 };
3517
3518 // Walk in IR order: GlobalOps first (they appear before functions in the
3519 // module body), then FuncOps. This matches OGCG's emission order.
3520 module.walk([&](cir::GlobalOp op) {
3521 handleArray(op.getSymNameAttr(), op.getAnnotationsAttr(), op.getLoc());
3522 });
3523 module.walk([&](cir::FuncOp op) {
3524 handleArray(op.getSymNameAttr(), op.getAnnotationsAttr(), op.getLoc());
3525 });
3526}
3527
3529 if (collectedAnnotations.empty())
3530 return;
3531
3532 mlir::MLIRContext *ctx = module.getContext();
3533 mlir::OpBuilder builder(ctx);
3534 builder.setInsertionPointToEnd(&module.getBodyRegion().back());
3535
3536 auto ptrTy = mlir::LLVM::LLVMPointerType::get(ctx);
3537 auto i32Ty = builder.getI32Type();
3538
3539 // Each entry: { ptr, ptr, ptr, i32, ptr }.
3540 auto entryTy = mlir::LLVM::LLVMStructType::getLiteral(
3541 ctx, {ptrTy, ptrTy, ptrTy, i32Ty, ptrTy});
3542 auto arrayTy =
3543 mlir::LLVM::LLVMArrayType::get(entryTy, collectedAnnotations.size());
3544
3545 mlir::Location moduleLoc = module.getLoc();
3546 auto annotationsGlobal = mlir::LLVM::GlobalOp::create(
3547 builder, moduleLoc, arrayTy, /*isConstant=*/false,
3548 mlir::LLVM::Linkage::Appending, "llvm.global.annotations",
3549 mlir::Attribute());
3550 annotationsGlobal.setSection(llvmMetadataSectionName);
3551
3552 // Strings/args constants must come *before* @llvm.global.annotations to
3553 // match OGCG output order. Insert them just before the annotations global.
3554 mlir::OpBuilder constsBuilder(ctx);
3555 constsBuilder.setInsertionPoint(annotationsGlobal);
3556
3557 llvm::StringMap<mlir::LLVM::GlobalOp> stringCache;
3558 llvm::StringMap<mlir::LLVM::GlobalOp> argStringCache;
3559 llvm::MapVector<mlir::ArrayAttr, mlir::LLVM::GlobalOp> argsCache;
3560
3561 // Build the initializer block of @llvm.global.annotations.
3562 annotationsGlobal.getRegion().push_back(new mlir::Block());
3563 mlir::OpBuilder initBuilder(ctx);
3564 initBuilder.setInsertionPointToEnd(annotationsGlobal.getInitializerBlock());
3565
3566 mlir::Value arrayVal =
3567 mlir::LLVM::UndefOp::create(initBuilder, moduleLoc, arrayTy);
3568
3569 for (auto [idx, entry] : llvm::enumerate(collectedAnnotations)) {
3570 mlir::Value entryVal =
3571 mlir::LLVM::UndefOp::create(initBuilder, moduleLoc, entryTy);
3572
3573 // Field 0: ptr to the annotated symbol. (Literal zero is ambiguous on
3574 // InsertValueOp::create, wrap in a SmallVector.)
3576 mlir::LLVM::AddressOfOp symAddr = mlir::LLVM::AddressOfOp::create(
3577 initBuilder, moduleLoc, ptrTy, entry.symName.getValue());
3578 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3579 entryVal, symAddr, zero);
3580
3581 // Field 1: ptr to the annotation name string.
3582 mlir::LLVM::GlobalOp nameGlobal = getOrCreateAnnotationStringGlobal(
3583 constsBuilder, moduleLoc, module, entry.annotation.getName().getValue(),
3584 stringCache, /*isArg=*/false);
3585 mlir::LLVM::AddressOfOp nameAddr = mlir::LLVM::AddressOfOp::create(
3586 initBuilder, moduleLoc, ptrTy, nameGlobal.getSymName());
3587 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3588 entryVal, nameAddr, 1);
3589
3590 // Fields 2 and 3: ptr to filename string and line number.
3591 auto [filename, line] = extractFileLine(entry.loc);
3592 mlir::LLVM::GlobalOp fileGlobal = getOrCreateAnnotationStringGlobal(
3593 constsBuilder, moduleLoc, module, filename, stringCache,
3594 /*isArg=*/false);
3595 mlir::LLVM::AddressOfOp fileAddr = mlir::LLVM::AddressOfOp::create(
3596 initBuilder, moduleLoc, ptrTy, fileGlobal.getSymName());
3597 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3598 entryVal, fileAddr, 2);
3599 mlir::LLVM::ConstantOp lineConst =
3600 mlir::LLVM::ConstantOp::create(initBuilder, moduleLoc, i32Ty, line);
3601 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3602 entryVal, lineConst, 3);
3603
3604 // Field 4: ptr to args, or null if none.
3605 mlir::ArrayAttr args = entry.annotation.getArgs();
3606 mlir::Value argsField;
3607 if (!args || args.empty()) {
3608 argsField = mlir::LLVM::ZeroOp::create(initBuilder, moduleLoc, ptrTy);
3609 } else {
3610 mlir::LLVM::GlobalOp argsGlobal = getOrCreateAnnotationArgsVar(
3611 constsBuilder, moduleLoc, module, args, argStringCache, argsCache);
3612 argsField = mlir::LLVM::AddressOfOp::create(initBuilder, moduleLoc, ptrTy,
3613 argsGlobal.getSymName());
3614 }
3615 entryVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3616 entryVal, argsField, 4);
3617
3618 arrayVal = mlir::LLVM::InsertValueOp::create(initBuilder, moduleLoc,
3619 arrayVal, entryVal, idx);
3620 }
3621
3622 mlir::LLVM::ReturnOp::create(initBuilder, moduleLoc, arrayVal);
3623}
3624
3626 LLVMBlockAddressInfo &blockInfoAddr) {
3627
3628 mlir::ModuleOp module = getOperation();
3629 mlir::OpBuilder opBuilder(module.getContext());
3630 for (auto &[blockAddOp, blockInfo] :
3631 blockInfoAddr.getUnresolvedBlockAddress()) {
3632 mlir::LLVM::BlockTagOp resolvedLabel =
3633 blockInfoAddr.lookupBlockTag(blockInfo);
3634 assert(resolvedLabel && "expected BlockTagOp to already be emitted");
3635 mlir::FlatSymbolRefAttr fnSym = blockInfo.getFunc();
3636 auto blkAddTag = mlir::LLVM::BlockAddressAttr::get(
3637 opBuilder.getContext(), fnSym, resolvedLabel.getTagAttr());
3638 blockAddOp.setBlockAddrAttr(blkAddTag);
3639 }
3640 blockInfoAddr.clearUnresolvedMap();
3641}
3642
3643void ConvertCIRToLLVMPass::processCIRAttrs(mlir::ModuleOp module) {
3644 // Lower the module attributes to LLVM equivalents.
3645 if (mlir::Attribute tripleAttr =
3646 module->getAttr(cir::CIRDialect::getTripleAttrName()))
3647 module->setAttr(mlir::LLVM::LLVMDialect::getTargetTripleAttrName(),
3648 tripleAttr);
3649
3650 if (mlir::Attribute asmAttr =
3651 module->getAttr(cir::CIRDialect::getModuleLevelAsmAttrName()))
3652 module->setAttr(mlir::LLVM::LLVMDialect::getModuleLevelAsmAttrName(),
3653 asmAttr);
3654}
3655
3657 llvm::TimeTraceScope scope("Convert CIR to LLVM Pass");
3658
3659 mlir::ModuleOp module = getOperation();
3660 mlir::DataLayout dl(module);
3661 mlir::LLVMTypeConverter converter(&getContext());
3662 prepareTypeConverter(converter, dl);
3663
3664 /// Tracks the state required to lower CIR `LabelOp` and `BlockAddressOp`.
3665 /// Maps labels to their corresponding `BlockTagOp` and keeps bookkeeping
3666 /// of unresolved `BlockAddressOp`s until they are matched with the
3667 /// corresponding `BlockTagOp` in `resolveBlockAddressOp`.
3668 LLVMBlockAddressInfo blockInfoAddr;
3669 /// Cached symbol table collection used by call lowering patterns to avoid
3670 /// repeated O(M) module-wide symbol scans for every call site.
3671 mlir::SymbolTableCollection symbolTables;
3672 mlir::RewritePatternSet patterns(&getContext());
3673 patterns.add<CIRToLLVMBlockAddressOpLowering, CIRToLLVMLabelOpLowering>(
3674 converter, patterns.getContext(), dl, blockInfoAddr);
3675 patterns.add<CIRToLLVMCallOpLowering, CIRToLLVMTryCallOpLowering>(
3676 converter, patterns.getContext(), dl, symbolTables);
3677
3678 patterns.add<
3679#define GET_LLVM_LOWERING_PATTERNS_LIST
3680#include "clang/CIR/Dialect/IR/CIRLowering.inc"
3681#undef GET_LLVM_LOWERING_PATTERNS_LIST
3682 >(converter, patterns.getContext(), dl);
3683
3684 processCIRAttrs(module);
3685
3686 // Collect annotation info from cir.func / cir.global before conversion;
3687 // the annotations attribute is filtered out during FuncOp/GlobalOp lowering.
3689
3690 mlir::ConversionTarget target(getContext());
3691 target.addLegalOp<mlir::ModuleOp>();
3692 target.addLegalDialect<mlir::LLVM::LLVMDialect>();
3693 mlir::configureOpenMPToLLVMConversionLegality(target, converter);
3694 target.addLegalDialect<mlir::omp::OpenMPDialect>();
3695 mlir::populateOpenMPToLLVMConversionPatterns(converter, patterns);
3696 target.addIllegalDialect<mlir::BuiltinDialect, cir::CIRDialect,
3697 mlir::func::FuncDialect>();
3698
3700 ops.push_back(module);
3701 cir::collectUnreachable(module, ops);
3702
3703 if (failed(applyPartialConversion(ops, target, std::move(patterns))))
3704 signalPassFailure();
3705
3706 // Emit the llvm.global_ctors array.
3707 buildCtorDtorList(module, cir::CIRDialect::getGlobalCtorsAttrName(),
3708 "llvm.global_ctors", [](mlir::Attribute attr) {
3709 auto ctorAttr = mlir::cast<cir::GlobalCtorAttr>(attr);
3710 return std::make_pair(ctorAttr.getName(),
3711 ctorAttr.getPriority());
3712 });
3713 // Emit the llvm.global_dtors array.
3714 buildCtorDtorList(module, cir::CIRDialect::getGlobalDtorsAttrName(),
3715 "llvm.global_dtors", [](mlir::Attribute attr) {
3716 auto dtorAttr = mlir::cast<cir::GlobalDtorAttr>(attr);
3717 return std::make_pair(dtorAttr.getName(),
3718 dtorAttr.getPriority());
3719 });
3720 // Emit @llvm.global.annotations from the previously-collected entries.
3722
3723 resolveBlockAddressOp(blockInfoAddr);
3724}
3725
3726mlir::LogicalResult CIRToLLVMBrOpLowering::matchAndRewrite(
3727 cir::BrOp op, OpAdaptor adaptor,
3728 mlir::ConversionPatternRewriter &rewriter) const {
3729 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(op, adaptor.getOperands(),
3730 op.getDest());
3731 return mlir::LogicalResult::success();
3732}
3733
3734mlir::LogicalResult CIRToLLVMGetMemberOpLowering::matchAndRewrite(
3735 cir::GetMemberOp op, OpAdaptor adaptor,
3736 mlir::ConversionPatternRewriter &rewriter) const {
3737 mlir::Type llResTy = getTypeConverter()->convertType(op.getType());
3738 mlir::Type pointee = op.getAddrTy().getPointee();
3739
3740 if (mlir::isa<cir::UnionType>(pointee)) {
3741 // Union members share the address space, so we just need a bitcast to
3742 // conform to type-checking.
3743 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(op, llResTy,
3744 adaptor.getAddr());
3745 return mlir::success();
3746 }
3747
3748 auto structTy = mlir::cast<cir::StructType>(pointee);
3749 // Since the base address is a pointer to an aggregate, the first offset
3750 // is always zero. The second offset tells us which member it will access.
3751 llvm::SmallVector<mlir::LLVM::GEPArg, 2> offset{0, op.getIndex()};
3752 const mlir::Type elementTy = getTypeConverter()->convertType(structTy);
3753 // Struct member accesses are always inbounds and nuw: the base pointer
3754 // is valid and the member offset is a positive, constant offset within
3755 // the struct layout, so it cannot wrap. This matches LLVM's
3756 // IRBuilder::CreateStructGEP.
3757 mlir::LLVM::GEPNoWrapFlags flags =
3758 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
3759 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
3760 op, llResTy, elementTy, adaptor.getAddr(), offset, flags);
3761 return mlir::success();
3762}
3763
3764mlir::LogicalResult CIRToLLVMExtractMemberOpLowering::matchAndRewrite(
3765 cir::ExtractMemberOp op, OpAdaptor adaptor,
3766 mlir::ConversionPatternRewriter &rewriter) const {
3767 std::int64_t indices[1] = {static_cast<std::int64_t>(op.getIndex())};
3768
3769 if (mlir::isa<cir::UnionType>(op.getRecord().getType())) {
3770 op.emitError("cir.extract_member cannot extract member from a union");
3771 return mlir::failure();
3772 }
3773
3774 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
3775 op, adaptor.getRecord(), indices);
3776 return mlir::success();
3777}
3778
3779mlir::LogicalResult CIRToLLVMInsertMemberOpLowering::matchAndRewrite(
3780 cir::InsertMemberOp op, OpAdaptor adaptor,
3781 mlir::ConversionPatternRewriter &rewriter) const {
3782 std::int64_t indecies[1] = {static_cast<std::int64_t>(op.getIndex())};
3783
3784 if (mlir::isa<cir::UnionType>(op.getRecord().getType())) {
3785 op.emitError("cir.update_member cannot update member of a union");
3786 return mlir::failure();
3787 }
3788
3789 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
3790 op, adaptor.getRecord(), adaptor.getValue(), indecies);
3791 return mlir::success();
3792}
3793
3794void createLLVMFuncOpIfNotExist(mlir::ConversionPatternRewriter &rewriter,
3795 mlir::Operation *srcOp, llvm::StringRef fnName,
3796 mlir::Type fnTy,
3797 mlir::ArrayAttr argAttrs = nullptr,
3798 mlir::ArrayAttr resAttrs = nullptr) {
3799 mlir::ModuleOp modOp = srcOp->getParentOfType<mlir::ModuleOp>();
3800 mlir::Operation *sourceSymbol =
3801 mlir::SymbolTable::lookupSymbolIn(modOp, fnName);
3802 if (!sourceSymbol) {
3803 mlir::OpBuilder::InsertionGuard guard(rewriter);
3804 auto enclosingFnOp = srcOp->getParentOfType<mlir::LLVM::LLVMFuncOp>();
3805 rewriter.setInsertionPoint(enclosingFnOp);
3806 auto fn =
3807 mlir::LLVM::LLVMFuncOp::create(rewriter, srcOp->getLoc(), fnName, fnTy);
3808 if (argAttrs)
3809 fn.setArgAttrsAttr(argAttrs);
3810 if (resAttrs)
3811 fn.setResAttrsAttr(resAttrs);
3812 }
3813}
3814
3815mlir::LogicalResult CIRToLLVMThrowOpLowering::matchAndRewrite(
3816 cir::ThrowOp op, OpAdaptor adaptor,
3817 mlir::ConversionPatternRewriter &rewriter) const {
3818 mlir::Location loc = op.getLoc();
3819 auto voidTy = mlir::LLVM::LLVMVoidType::get(getContext());
3820
3821 if (op.rethrows()) {
3822 auto funcTy = mlir::LLVM::LLVMFunctionType::get(voidTy, {});
3823
3824 // Get or create `declare void @__cxa_rethrow()`
3825 const llvm::StringRef functionName = "__cxa_rethrow";
3826 createLLVMFuncOpIfNotExist(rewriter, op, functionName, funcTy);
3827
3828 auto cxaRethrow = mlir::LLVM::CallOp::create(
3829 rewriter, loc, mlir::TypeRange{}, functionName);
3830
3831 rewriter.replaceOp(op, cxaRethrow);
3832 return mlir::success();
3833 }
3834
3835 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
3836 auto fnTy = mlir::LLVM::LLVMFunctionType::get(
3837 voidTy, {llvmPtrTy, llvmPtrTy, llvmPtrTy});
3838
3839 // Get or create `declare void @__cxa_throw(ptr, ptr, ptr)`
3840 const llvm::StringRef fnName = "__cxa_throw";
3841 createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
3842
3843 mlir::Value typeInfo = mlir::LLVM::AddressOfOp::create(
3844 rewriter, loc, mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
3845 adaptor.getTypeInfoAttr());
3846
3847 mlir::Value dtor;
3848 if (op.getDtor()) {
3849 dtor = mlir::LLVM::AddressOfOp::create(rewriter, loc, llvmPtrTy,
3850 adaptor.getDtorAttr());
3851 } else {
3852 dtor = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
3853 }
3854
3855 auto cxaThrowCall = mlir::LLVM::CallOp::create(
3856 rewriter, loc, mlir::TypeRange{}, fnName,
3857 mlir::ValueRange{adaptor.getExceptionPtr(), typeInfo, dtor});
3858
3859 rewriter.replaceOp(op, cxaThrowCall);
3860 return mlir::success();
3861}
3862
3863mlir::LogicalResult CIRToLLVMAllocExceptionOpLowering::matchAndRewrite(
3864 cir::AllocExceptionOp op, OpAdaptor adaptor,
3865 mlir::ConversionPatternRewriter &rewriter) const {
3866 // Get or create `declare ptr @__cxa_allocate_exception(i64)`
3867 StringRef fnName = "__cxa_allocate_exception";
3868 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
3869 auto int64Ty = mlir::IntegerType::get(rewriter.getContext(), 64);
3870 auto fnTy = mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {int64Ty});
3871
3872 createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy);
3873 auto exceptionSize = mlir::LLVM::ConstantOp::create(rewriter, op.getLoc(),
3874 adaptor.getSizeAttr());
3875
3876 auto allocaExceptionCall = mlir::LLVM::CallOp::create(
3877 rewriter, op.getLoc(), mlir::TypeRange{llvmPtrTy}, fnName,
3878 mlir::ValueRange{exceptionSize});
3879
3880 rewriter.replaceOp(op, allocaExceptionCall);
3881 return mlir::success();
3882}
3883
3884static mlir::LLVM::LLVMStructType
3885getLLVMLandingPadStructTy(mlir::ConversionPatternRewriter &rewriter) {
3886 // Create the landing pad type: struct { ptr, i32 }
3887 mlir::MLIRContext *ctx = rewriter.getContext();
3888 auto llvmPtr = mlir::LLVM::LLVMPointerType::get(ctx);
3889 llvm::SmallVector<mlir::Type> structFields = {llvmPtr, rewriter.getI32Type()};
3890 return mlir::LLVM::LLVMStructType::getLiteral(ctx, structFields);
3891}
3892
3893mlir::LogicalResult CIRToLLVMEhInflightOpLowering::matchAndRewrite(
3894 cir::EhInflightOp op, OpAdaptor adaptor,
3895 mlir::ConversionPatternRewriter &rewriter) const {
3896 auto llvmFn = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
3897 assert(llvmFn && "expected LLVM function parent");
3898 mlir::Block *entryBlock = &llvmFn.getRegion().front();
3899 assert(entryBlock->isEntryBlock());
3900
3901 mlir::ArrayAttr catchListAttr = op.getCatchTypeListAttr();
3902 mlir::SmallVector<mlir::Value> catchSymAddrs;
3903
3904 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
3905 mlir::Location loc = op.getLoc();
3906
3907 // %landingpad = landingpad { ptr, i32 }
3908 // Note that since llvm.landingpad has to be the first operation on the
3909 // block, any needed value for its operands has to be added somewhere else.
3910 if (catchListAttr) {
3911 // catch ptr @_ZTIi
3912 // catch ptr @_ZTIPKc
3913 for (mlir::Attribute catchAttr : catchListAttr) {
3914 auto symAttr = cast<mlir::FlatSymbolRefAttr>(catchAttr);
3915 // Generate `llvm.mlir.addressof` for each symbol, and place those
3916 // operations in the LLVM function entry basic block.
3917 mlir::OpBuilder::InsertionGuard guard(rewriter);
3918 rewriter.setInsertionPointToStart(entryBlock);
3919 mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
3920 rewriter, loc, llvmPtrTy, symAttr.getValue());
3921 catchSymAddrs.push_back(addrOp);
3922 }
3923 }
3924
3925 // Emit a catch-all clause (catch ptr null) when:
3926 // - The catch_all attribute is set (typed catches + catch-all), or
3927 // - No typed catches and no cleanup (legacy pure catch-all form)
3928 if (op.getCatchAll() || (!catchListAttr && !op.getCleanup())) {
3929 mlir::OpBuilder::InsertionGuard guard(rewriter);
3930 rewriter.setInsertionPointToStart(entryBlock);
3931 mlir::Value nullOp = mlir::LLVM::ZeroOp::create(rewriter, loc, llvmPtrTy);
3932 catchSymAddrs.push_back(nullOp);
3933 }
3934
3935 // %slot = extractvalue { ptr, i32 } %x, 0
3936 // %selector = extractvalue { ptr, i32 } %x, 1
3937 mlir::LLVM::LLVMStructType llvmLandingPadStructTy =
3938 getLLVMLandingPadStructTy(rewriter);
3939 auto landingPadOp = mlir::LLVM::LandingpadOp::create(
3940 rewriter, loc, llvmLandingPadStructTy, catchSymAddrs);
3941
3942 // The LLVM cleanup flag is only needed when there is no catch-all handler,
3943 // since catch-all (catch ptr null) already ensures the personality function
3944 // enters the landing pad for all exception types.
3945 if (op.getCleanup() && !op.getCatchAll())
3946 landingPadOp.setCleanup(true);
3947
3948 mlir::Value slot =
3949 mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 0);
3950 mlir::Value selector =
3951 mlir::LLVM::ExtractValueOp::create(rewriter, loc, landingPadOp, 1);
3952 rewriter.replaceOp(op, mlir::ValueRange{slot, selector});
3953
3954 return mlir::success();
3955}
3956
3957mlir::LogicalResult CIRToLLVMResumeFlatOpLowering::matchAndRewrite(
3958 cir::ResumeFlatOp op, OpAdaptor adaptor,
3959 mlir::ConversionPatternRewriter &rewriter) const {
3960 // %lpad.val = insertvalue { ptr, i32 } poison, ptr %exception_ptr, 0
3961 // %lpad.val2 = insertvalue { ptr, i32 } %lpad.val, i32 %selector, 1
3962 // resume { ptr, i32 } %lpad.val2
3963 mlir::Type llvmLandingPadStructTy = getLLVMLandingPadStructTy(rewriter);
3964 mlir::Value poison = mlir::LLVM::PoisonOp::create(rewriter, op.getLoc(),
3965 llvmLandingPadStructTy);
3966
3967 SmallVector<int64_t> slotIdx = {0};
3968 mlir::Value slot = mlir::LLVM::InsertValueOp::create(
3969 rewriter, op.getLoc(), poison, adaptor.getExceptionPtr(), slotIdx);
3970
3971 SmallVector<int64_t> selectorIdx = {1};
3972 mlir::Value selector = mlir::LLVM::InsertValueOp::create(
3973 rewriter, op.getLoc(), slot, adaptor.getTypeId(), selectorIdx);
3974
3975 rewriter.replaceOpWithNewOp<mlir::LLVM::ResumeOp>(op, selector);
3976 return mlir::success();
3977}
3978
3979mlir::LogicalResult CIRToLLVMEhTypeIdOpLowering::matchAndRewrite(
3980 cir::EhTypeIdOp op, OpAdaptor adaptor,
3981 mlir::ConversionPatternRewriter &rewriter) const {
3982 mlir::Value addrOp = mlir::LLVM::AddressOfOp::create(
3983 rewriter, op.getLoc(),
3984 mlir::LLVM::LLVMPointerType::get(rewriter.getContext()),
3985 op.getTypeSymAttr());
3986 rewriter.replaceOpWithNewOp<mlir::LLVM::EhTypeidForOp>(
3987 op, rewriter.getI32Type(), addrOp);
3988 return mlir::success();
3989}
3990
3991mlir::LogicalResult CIRToLLVMEhSetjmpOpLowering::matchAndRewrite(
3992 cir::EhSetjmpOp op, OpAdaptor adaptor,
3993 mlir::ConversionPatternRewriter &rewriter) const {
3994 mlir::Type returnType = typeConverter->convertType(op.getType());
3995 mlir::LLVM::CallIntrinsicOp newOp =
3996 createCallLLVMIntrinsicOp(rewriter, op.getLoc(), "llvm.eh.sjlj.setjmp",
3997 returnType, adaptor.getEnv());
3998 rewriter.replaceOp(op, newOp);
3999 return mlir::success();
4000}
4001
4002mlir::LogicalResult CIRToLLVMEhLongjmpOpLowering::matchAndRewrite(
4003 cir::EhLongjmpOp op, OpAdaptor adaptor,
4004 mlir::ConversionPatternRewriter &rewriter) const {
4005 replaceOpWithCallLLVMIntrinsicOp(rewriter, op, "llvm.eh.sjlj.longjmp",
4006 /*resultTy=*/{}, adaptor.getOperands());
4007 return mlir::success();
4008}
4009
4010mlir::LogicalResult CIRToLLVMTrapOpLowering::matchAndRewrite(
4011 cir::TrapOp op, OpAdaptor adaptor,
4012 mlir::ConversionPatternRewriter &rewriter) const {
4013 mlir::Location loc = op->getLoc();
4014 rewriter.eraseOp(op);
4015
4016 mlir::LLVM::Trap::create(rewriter, loc);
4017
4018 // Note that the call to llvm.trap is not a terminator in LLVM dialect.
4019 // So we must emit an additional llvm.unreachable to terminate the current
4020 // block.
4021 mlir::LLVM::UnreachableOp::create(rewriter, loc);
4022
4023 return mlir::success();
4024}
4025
4026static mlir::Value
4027getValueForVTableSymbol(mlir::Operation *op,
4028 mlir::ConversionPatternRewriter &rewriter,
4029 const mlir::TypeConverter *converter,
4030 mlir::FlatSymbolRefAttr nameAttr, mlir::Type &eltType) {
4031 auto module = op->getParentOfType<mlir::ModuleOp>();
4032 mlir::Operation *symbol = mlir::SymbolTable::lookupSymbolIn(module, nameAttr);
4033 if (auto llvmSymbol = mlir::dyn_cast<mlir::LLVM::GlobalOp>(symbol)) {
4034 eltType = llvmSymbol.getType();
4035 } else if (auto cirSymbol = mlir::dyn_cast<cir::GlobalOp>(symbol)) {
4036 eltType = converter->convertType(cirSymbol.getSymType());
4037 } else {
4038 op->emitError() << "unexpected symbol type for " << symbol;
4039 return {};
4040 }
4041
4042 return mlir::LLVM::AddressOfOp::create(
4043 rewriter, op->getLoc(),
4044 mlir::LLVM::LLVMPointerType::get(op->getContext()), nameAttr.getValue());
4045}
4046
4047mlir::LogicalResult CIRToLLVMVTableAddrPointOpLowering::matchAndRewrite(
4048 cir::VTableAddrPointOp op, OpAdaptor adaptor,
4049 mlir::ConversionPatternRewriter &rewriter) const {
4050 const mlir::TypeConverter *converter = getTypeConverter();
4051 mlir::Type targetType = converter->convertType(op.getType());
4053 mlir::Type eltType;
4054 mlir::Value symAddr = getValueForVTableSymbol(op, rewriter, converter,
4055 op.getNameAttr(), eltType);
4056 if (!symAddr)
4057 return op.emitError() << "Unable to get value for vtable symbol";
4058
4060 0, op.getAddressPointAttr().getIndex(),
4061 op.getAddressPointAttr().getOffset()};
4062
4063 assert(eltType && "Shouldn't ever be missing an eltType here");
4064 mlir::LLVM::GEPNoWrapFlags inboundsNuw =
4065 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
4066 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(op, targetType, eltType,
4067 symAddr, offsets, inboundsNuw);
4068 return mlir::success();
4069}
4070
4071mlir::LogicalResult CIRToLLVMVTableGetVPtrOpLowering::matchAndRewrite(
4072 cir::VTableGetVPtrOp op, OpAdaptor adaptor,
4073 mlir::ConversionPatternRewriter &rewriter) const {
4074 // cir.vtable.get_vptr is equivalent to a bitcast from the source object
4075 // pointer to the vptr type. Since the LLVM dialect uses opaque pointers
4076 // we can just replace uses of this operation with the original pointer.
4077 mlir::Value srcVal = adaptor.getSrc();
4078 rewriter.replaceOp(op, srcVal);
4079 return mlir::success();
4080}
4081
4082mlir::LogicalResult CIRToLLVMVTableGetVirtualFnAddrOpLowering::matchAndRewrite(
4083 cir::VTableGetVirtualFnAddrOp op, OpAdaptor adaptor,
4084 mlir::ConversionPatternRewriter &rewriter) const {
4085 mlir::Type targetType = getTypeConverter()->convertType(op.getType());
4086 auto eltType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4089 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4090 op, targetType, eltType, adaptor.getVptr(), offsets,
4091 mlir::LLVM::GEPNoWrapFlags::inbounds);
4092 return mlir::success();
4093}
4094
4095mlir::LogicalResult CIRToLLVMVTTAddrPointOpLowering::matchAndRewrite(
4096 cir::VTTAddrPointOp op, OpAdaptor adaptor,
4097 mlir::ConversionPatternRewriter &rewriter) const {
4098 const mlir::Type resultType = getTypeConverter()->convertType(op.getType());
4100 mlir::Type eltType;
4101 mlir::Value llvmAddr = adaptor.getSymAddr();
4102
4103 if (op.getSymAddr()) {
4104 if (op.getOffset() == 0) {
4105 rewriter.replaceOp(op, {llvmAddr});
4106 return mlir::success();
4107 }
4108
4109 offsets.push_back(adaptor.getOffset());
4110 eltType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4111 } else {
4112 llvmAddr = getValueForVTableSymbol(op, rewriter, getTypeConverter(),
4113 op.getNameAttr(), eltType);
4114 assert(eltType && "Shouldn't ever be missing an eltType here");
4115 offsets.push_back(0);
4116 offsets.push_back(adaptor.getOffset());
4117 }
4118 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4119 op, resultType, eltType, llvmAddr, offsets,
4120 mlir::LLVM::GEPNoWrapFlags::inbounds);
4121 return mlir::success();
4122}
4123
4124mlir::LogicalResult CIRToLLVMVecCreateOpLowering::matchAndRewrite(
4125 cir::VecCreateOp op, OpAdaptor adaptor,
4126 mlir::ConversionPatternRewriter &rewriter) const {
4127 // Start with an 'undef' value for the vector. Then 'insertelement' for
4128 // each of the vector elements.
4129 const auto vecTy = mlir::cast<cir::VectorType>(op.getType());
4130 const mlir::Type llvmTy = typeConverter->convertType(vecTy);
4131 const mlir::Location loc = op.getLoc();
4132 mlir::Value result = mlir::LLVM::PoisonOp::create(rewriter, loc, llvmTy);
4133 assert(vecTy.getSize() == op.getElements().size() &&
4134 "cir.vec.create op count doesn't match vector type elements count");
4135
4136 for (uint64_t i = 0; i < vecTy.getSize(); ++i) {
4137 const mlir::Value indexValue =
4138 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), i);
4139 result = mlir::LLVM::InsertElementOp::create(
4140 rewriter, loc, result, adaptor.getElements()[i], indexValue);
4141 }
4142
4143 rewriter.replaceOp(op, result);
4144 return mlir::success();
4145}
4146
4147mlir::LogicalResult CIRToLLVMVecExtractOpLowering::matchAndRewrite(
4148 cir::VecExtractOp op, OpAdaptor adaptor,
4149 mlir::ConversionPatternRewriter &rewriter) const {
4150 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractElementOp>(
4151 op, adaptor.getVec(), adaptor.getIndex());
4152 return mlir::success();
4153}
4154
4155mlir::LogicalResult CIRToLLVMVecInsertOpLowering::matchAndRewrite(
4156 cir::VecInsertOp op, OpAdaptor adaptor,
4157 mlir::ConversionPatternRewriter &rewriter) const {
4158 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertElementOp>(
4159 op, adaptor.getVec(), adaptor.getValue(), adaptor.getIndex());
4160 return mlir::success();
4161}
4162
4163mlir::LogicalResult CIRToLLVMVecCmpOpLowering::matchAndRewrite(
4164 cir::VecCmpOp op, OpAdaptor adaptor,
4165 mlir::ConversionPatternRewriter &rewriter) const {
4166 mlir::Type elementType = elementTypeIfVector(op.getLhs().getType());
4167 mlir::Value bitResult;
4168 if (auto intType = mlir::dyn_cast<cir::IntType>(elementType)) {
4169 bitResult = mlir::LLVM::ICmpOp::create(
4170 rewriter, op.getLoc(),
4171 convertCmpKindToICmpPredicate(op.getKind(), intType.isSigned()),
4172 adaptor.getLhs(), adaptor.getRhs());
4173 } else if (mlir::isa<cir::FPTypeInterface>(elementType)) {
4174 bitResult = mlir::LLVM::FCmpOp::create(
4175 rewriter, op.getLoc(), convertCmpKindToFCmpPredicate(op.getKind()),
4176 adaptor.getLhs(), adaptor.getRhs());
4177 } else {
4178 return op.emitError() << "unsupported type for VecCmpOp: " << elementType;
4179 }
4180
4181 // LLVM IR vector comparison returns a vector of i1. This one-bit vector
4182 // must be sign-extended to the correct result type, unless a vector of i1 is
4183 // the type we need.
4184 if (cast<cir::IntType>(cast<cir::VectorType>(op.getType()).getElementType())
4185 .getWidth() > 1)
4186 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(
4187 op, typeConverter->convertType(op.getType()), bitResult);
4188 else
4189 rewriter.replaceOp(op, bitResult);
4190 return mlir::success();
4191}
4192
4193mlir::LogicalResult CIRToLLVMVecSplatOpLowering::matchAndRewrite(
4194 cir::VecSplatOp op, OpAdaptor adaptor,
4195 mlir::ConversionPatternRewriter &rewriter) const {
4196 // Vector splat can be implemented with an `insertelement` and a
4197 // `shufflevector`, which is better than an `insertelement` for each
4198 // element in the vector. Start with an undef vector. Insert the value into
4199 // the first element. Then use a `shufflevector` with a mask of all 0 to
4200 // fill out the entire vector with that value.
4201 cir::VectorType vecTy = op.getType();
4202 mlir::Type llvmTy = typeConverter->convertType(vecTy);
4203 mlir::Location loc = op.getLoc();
4204 mlir::Value poison = mlir::LLVM::PoisonOp::create(rewriter, loc, llvmTy);
4205
4206 mlir::Value elementValue = adaptor.getValue();
4207 if (elementValue.getDefiningOp<mlir::LLVM::PoisonOp>()) {
4208 // If the splat value is poison, then we can just use poison value
4209 // for the entire vector.
4210 rewriter.replaceOp(op, poison);
4211 return mlir::success();
4212 }
4213
4214 if (auto constValue = elementValue.getDefiningOp<mlir::LLVM::ConstantOp>()) {
4215 if (auto intAttr = dyn_cast<mlir::IntegerAttr>(constValue.getValue())) {
4216 mlir::DenseIntElementsAttr denseVec = mlir::DenseIntElementsAttr::get(
4217 mlir::cast<mlir::ShapedType>(llvmTy), intAttr.getValue());
4218 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
4219 op, denseVec.getType(), denseVec);
4220 return mlir::success();
4221 }
4222
4223 if (auto fpAttr = dyn_cast<mlir::FloatAttr>(constValue.getValue())) {
4224 mlir::DenseFPElementsAttr denseVec = mlir::DenseFPElementsAttr::get(
4225 mlir::cast<mlir::ShapedType>(llvmTy), fpAttr.getValue());
4226 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
4227 op, denseVec.getType(), denseVec);
4228 return mlir::success();
4229 }
4230 }
4231
4232 mlir::Value indexValue =
4233 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), 0);
4234 mlir::Value oneElement = mlir::LLVM::InsertElementOp::create(
4235 rewriter, loc, poison, elementValue, indexValue);
4236 SmallVector<int32_t> zeroValues(vecTy.getSize(), 0);
4237 rewriter.replaceOpWithNewOp<mlir::LLVM::ShuffleVectorOp>(op, oneElement,
4238 poison, zeroValues);
4239 return mlir::success();
4240}
4241
4242mlir::LogicalResult CIRToLLVMVecShuffleOpLowering::matchAndRewrite(
4243 cir::VecShuffleOp op, OpAdaptor adaptor,
4244 mlir::ConversionPatternRewriter &rewriter) const {
4245 // LLVM::ShuffleVectorOp takes an ArrayRef of int for the list of indices.
4246 // Convert the ClangIR ArrayAttr of IntAttr constants into a
4247 // SmallVector<int>.
4248 SmallVector<int, 8> indices;
4249 std::transform(
4250 op.getIndices().begin(), op.getIndices().end(),
4251 std::back_inserter(indices), [](mlir::Attribute intAttr) {
4252 return mlir::cast<cir::IntAttr>(intAttr).getValue().getSExtValue();
4253 });
4254 rewriter.replaceOpWithNewOp<mlir::LLVM::ShuffleVectorOp>(
4255 op, adaptor.getVec1(), adaptor.getVec2(), indices);
4256 return mlir::success();
4257}
4258
4259mlir::LogicalResult CIRToLLVMVecShuffleDynamicOpLowering::matchAndRewrite(
4260 cir::VecShuffleDynamicOp op, OpAdaptor adaptor,
4261 mlir::ConversionPatternRewriter &rewriter) const {
4262 // LLVM IR does not have an operation that corresponds to this form of
4263 // the built-in.
4264 // __builtin_shufflevector(V, I)
4265 // is implemented as this pseudocode, where the for loop is unrolled
4266 // and N is the number of elements:
4267 //
4268 // result = undef
4269 // maskbits = NextPowerOf2(N - 1)
4270 // masked = I & maskbits
4271 // for (i in 0 <= i < N)
4272 // result[i] = V[masked[i]]
4273 mlir::Location loc = op.getLoc();
4274 mlir::Value input = adaptor.getVec();
4275 mlir::Type llvmIndexVecType =
4276 getTypeConverter()->convertType(op.getIndices().getType());
4277 mlir::Type llvmIndexType = getTypeConverter()->convertType(
4278 elementTypeIfVector(op.getIndices().getType()));
4279 uint64_t numElements =
4280 mlir::cast<cir::VectorType>(op.getVec().getType()).getSize();
4281
4282 uint64_t maskBits = llvm::NextPowerOf2(numElements - 1) - 1;
4283 mlir::Value maskValue = mlir::LLVM::ConstantOp::create(
4284 rewriter, loc, llvmIndexType,
4285 rewriter.getIntegerAttr(llvmIndexType, maskBits));
4286 mlir::Value maskVector =
4287 mlir::LLVM::UndefOp::create(rewriter, loc, llvmIndexVecType);
4288
4289 for (uint64_t i = 0; i < numElements; ++i) {
4290 mlir::Value idxValue =
4291 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), i);
4292 maskVector = mlir::LLVM::InsertElementOp::create(rewriter, loc, maskVector,
4293 maskValue, idxValue);
4294 }
4295
4296 mlir::Value maskedIndices = mlir::LLVM::AndOp::create(
4297 rewriter, loc, llvmIndexVecType, adaptor.getIndices(), maskVector);
4298 mlir::Value result = mlir::LLVM::UndefOp::create(
4299 rewriter, loc, getTypeConverter()->convertType(op.getVec().getType()));
4300 for (uint64_t i = 0; i < numElements; ++i) {
4301 mlir::Value iValue =
4302 mlir::LLVM::ConstantOp::create(rewriter, loc, rewriter.getI64Type(), i);
4303 mlir::Value indexValue = mlir::LLVM::ExtractElementOp::create(
4304 rewriter, loc, maskedIndices, iValue);
4305 mlir::Value valueAtIndex =
4306 mlir::LLVM::ExtractElementOp::create(rewriter, loc, input, indexValue);
4307 result = mlir::LLVM::InsertElementOp::create(rewriter, loc, result,
4308 valueAtIndex, iValue);
4309 }
4310 rewriter.replaceOp(op, result);
4311 return mlir::success();
4312}
4313
4314mlir::LogicalResult CIRToLLVMVecTernaryOpLowering::matchAndRewrite(
4315 cir::VecTernaryOp op, OpAdaptor adaptor,
4316 mlir::ConversionPatternRewriter &rewriter) const {
4317 // Convert `cond` into a vector of i1, then use that in a `select` op.
4318 mlir::Value bitVec = mlir::LLVM::ICmpOp::create(
4319 rewriter, op.getLoc(), mlir::LLVM::ICmpPredicate::ne, adaptor.getCond(),
4320 mlir::LLVM::ZeroOp::create(
4321 rewriter, op.getCond().getLoc(),
4322 typeConverter->convertType(op.getCond().getType())));
4323 rewriter.replaceOpWithNewOp<mlir::LLVM::SelectOp>(
4324 op, bitVec, adaptor.getLhs(), adaptor.getRhs());
4325 return mlir::success();
4326}
4327
4328mlir::LogicalResult CIRToLLVMComplexAddOpLowering::matchAndRewrite(
4329 cir::ComplexAddOp op, OpAdaptor adaptor,
4330 mlir::ConversionPatternRewriter &rewriter) const {
4331 mlir::Value lhs = adaptor.getLhs();
4332 mlir::Value rhs = adaptor.getRhs();
4333 mlir::Location loc = op.getLoc();
4334
4335 auto complexType = mlir::cast<cir::ComplexType>(op.getLhs().getType());
4336 mlir::Type complexElemTy =
4337 getTypeConverter()->convertType(complexType.getElementType());
4338 auto lhsReal = mlir::LLVM::ExtractValueOp::create(
4339 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{0}));
4340 auto lhsImag = mlir::LLVM::ExtractValueOp::create(
4341 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{1}));
4342 auto rhsReal = mlir::LLVM::ExtractValueOp::create(
4343 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{0}));
4344 auto rhsImag = mlir::LLVM::ExtractValueOp::create(
4345 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{1}));
4346
4347 mlir::Value newReal;
4348 mlir::Value newImag;
4349 if (complexElemTy.isInteger()) {
4350 newReal = mlir::LLVM::AddOp::create(rewriter, loc, complexElemTy, lhsReal,
4351 rhsReal);
4352 newImag = mlir::LLVM::AddOp::create(rewriter, loc, complexElemTy, lhsImag,
4353 rhsImag);
4354 } else {
4357 newReal = mlir::LLVM::FAddOp::create(rewriter, loc, complexElemTy, lhsReal,
4358 rhsReal);
4359 newImag = mlir::LLVM::FAddOp::create(rewriter, loc, complexElemTy, lhsImag,
4360 rhsImag);
4361 }
4362
4363 mlir::Type complexLLVMTy =
4364 getTypeConverter()->convertType(op.getResult().getType());
4365 auto initialComplex =
4366 mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), complexLLVMTy);
4367
4368 auto realComplex = mlir::LLVM::InsertValueOp::create(
4369 rewriter, op->getLoc(), initialComplex, newReal, ArrayRef(int64_t{0}));
4370
4371 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
4372 op, realComplex, newImag, ArrayRef(int64_t{1}));
4373
4374 return mlir::success();
4375}
4376
4377mlir::LogicalResult CIRToLLVMComplexCreateOpLowering::matchAndRewrite(
4378 cir::ComplexCreateOp op, OpAdaptor adaptor,
4379 mlir::ConversionPatternRewriter &rewriter) const {
4380 mlir::Type complexLLVMTy =
4381 getTypeConverter()->convertType(op.getResult().getType());
4382 auto initialComplex =
4383 mlir::LLVM::UndefOp::create(rewriter, op->getLoc(), complexLLVMTy);
4384
4385 auto realComplex = mlir::LLVM::InsertValueOp::create(
4386 rewriter, op->getLoc(), initialComplex, adaptor.getReal(),
4387 ArrayRef(int64_t{0}));
4388
4389 auto complex = mlir::LLVM::InsertValueOp::create(
4390 rewriter, op->getLoc(), realComplex, adaptor.getImag(),
4391 ArrayRef(int64_t{1}));
4392
4393 rewriter.replaceOp(op, complex);
4394 return mlir::success();
4395}
4396
4397mlir::LogicalResult CIRToLLVMComplexRealOpLowering::matchAndRewrite(
4398 cir::ComplexRealOp op, OpAdaptor adaptor,
4399 mlir::ConversionPatternRewriter &rewriter) const {
4400 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4401 mlir::Value operand = adaptor.getOperand();
4402 if (mlir::isa<cir::ComplexType>(op.getOperand().getType())) {
4403 operand = mlir::LLVM::ExtractValueOp::create(
4404 rewriter, op.getLoc(), resultLLVMTy, operand,
4406 }
4407 rewriter.replaceOp(op, operand);
4408 return mlir::success();
4409}
4410
4411mlir::LogicalResult CIRToLLVMComplexSubOpLowering::matchAndRewrite(
4412 cir::ComplexSubOp op, OpAdaptor adaptor,
4413 mlir::ConversionPatternRewriter &rewriter) const {
4414 mlir::Value lhs = adaptor.getLhs();
4415 mlir::Value rhs = adaptor.getRhs();
4416 mlir::Location loc = op.getLoc();
4417
4418 auto complexType = mlir::cast<cir::ComplexType>(op.getLhs().getType());
4419 mlir::Type complexElemTy =
4420 getTypeConverter()->convertType(complexType.getElementType());
4421 auto lhsReal = mlir::LLVM::ExtractValueOp::create(
4422 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{0}));
4423 auto lhsImag = mlir::LLVM::ExtractValueOp::create(
4424 rewriter, loc, complexElemTy, lhs, ArrayRef(int64_t{1}));
4425 auto rhsReal = mlir::LLVM::ExtractValueOp::create(
4426 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{0}));
4427 auto rhsImag = mlir::LLVM::ExtractValueOp::create(
4428 rewriter, loc, complexElemTy, rhs, ArrayRef(int64_t{1}));
4429
4430 mlir::Value newReal;
4431 mlir::Value newImag;
4432 if (complexElemTy.isInteger()) {
4433 newReal = mlir::LLVM::SubOp::create(rewriter, loc, complexElemTy, lhsReal,
4434 rhsReal);
4435 newImag = mlir::LLVM::SubOp::create(rewriter, loc, complexElemTy, lhsImag,
4436 rhsImag);
4437 } else {
4440 newReal = mlir::LLVM::FSubOp::create(rewriter, loc, complexElemTy, lhsReal,
4441 rhsReal);
4442 newImag = mlir::LLVM::FSubOp::create(rewriter, loc, complexElemTy, lhsImag,
4443 rhsImag);
4444 }
4445
4446 mlir::Type complexLLVMTy =
4447 getTypeConverter()->convertType(op.getResult().getType());
4448 auto initialComplex =
4449 mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), complexLLVMTy);
4450
4451 auto realComplex = mlir::LLVM::InsertValueOp::create(
4452 rewriter, op->getLoc(), initialComplex, newReal, ArrayRef(int64_t{0}));
4453
4454 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
4455 op, realComplex, newImag, ArrayRef(int64_t{1}));
4456
4457 return mlir::success();
4458}
4459
4460mlir::LogicalResult CIRToLLVMComplexImagOpLowering::matchAndRewrite(
4461 cir::ComplexImagOp op, OpAdaptor adaptor,
4462 mlir::ConversionPatternRewriter &rewriter) const {
4463 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4464 mlir::Value operand = adaptor.getOperand();
4465 mlir::Location loc = op.getLoc();
4466
4467 if (mlir::isa<cir::ComplexType>(op.getOperand().getType())) {
4468 operand = mlir::LLVM::ExtractValueOp::create(
4469 rewriter, loc, resultLLVMTy, operand, llvm::ArrayRef<std::int64_t>{1});
4470 } else {
4471 mlir::TypedAttr zeroAttr = rewriter.getZeroAttr(resultLLVMTy);
4472 operand =
4473 mlir::LLVM::ConstantOp::create(rewriter, loc, resultLLVMTy, zeroAttr);
4474 }
4475
4476 rewriter.replaceOp(op, operand);
4477 return mlir::success();
4478}
4479
4480mlir::IntegerType computeBitfieldIntType(mlir::Type storageType,
4481 mlir::MLIRContext *context,
4482 unsigned &storageSize) {
4483 return TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
4484 .Case<cir::ArrayType>([&](cir::ArrayType atTy) {
4485 storageSize = atTy.getSize() * 8;
4486 return mlir::IntegerType::get(context, storageSize);
4487 })
4488 .Case<cir::IntType>([&](cir::IntType intTy) {
4489 storageSize = intTy.getWidth();
4490 return mlir::IntegerType::get(context, storageSize);
4491 })
4492 .Default([](mlir::Type) -> mlir::IntegerType {
4493 llvm_unreachable(
4494 "Either ArrayType or IntType expected for bitfields storage");
4495 });
4496}
4497
4498mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite(
4499 cir::SetBitfieldOp op, OpAdaptor adaptor,
4500 mlir::ConversionPatternRewriter &rewriter) const {
4501 mlir::OpBuilder::InsertionGuard guard(rewriter);
4502 rewriter.setInsertionPoint(op);
4503
4504 cir::BitfieldInfoAttr info = op.getBitfieldInfo();
4505 uint64_t size = info.getSize();
4506 uint64_t offset = info.getOffset();
4507 mlir::Type storageType = info.getStorageType();
4508 mlir::MLIRContext *context = storageType.getContext();
4509
4510 unsigned storageSize = 0;
4511
4512 mlir::IntegerType intType =
4513 computeBitfieldIntType(storageType, context, storageSize);
4514
4515 mlir::Value srcVal = createIntCast(rewriter, adaptor.getSrc(), intType);
4516 unsigned srcWidth = storageSize;
4517 mlir::Value resultVal = srcVal;
4518
4519 if (storageSize != size) {
4520 assert(storageSize > size && "Invalid bitfield size.");
4521
4522 mlir::Value val = mlir::LLVM::LoadOp::create(
4523 rewriter, op.getLoc(), intType, adaptor.getAddr(), op.getAlignment(),
4524 op.getIsVolatile());
4525
4526 srcVal =
4527 createAnd(rewriter, srcVal, llvm::APInt::getLowBitsSet(srcWidth, size));
4528 resultVal = srcVal;
4529 srcVal = createShL(rewriter, srcVal, offset);
4530
4531 // Mask out the original value.
4532 val = createAnd(rewriter, val,
4533 ~llvm::APInt::getBitsSet(srcWidth, offset, offset + size));
4534
4535 // Or together the unchanged values and the source value.
4536 srcVal = mlir::LLVM::OrOp::create(rewriter, op.getLoc(), val, srcVal);
4537 }
4538
4539 mlir::LLVM::StoreOp::create(rewriter, op.getLoc(), srcVal, adaptor.getAddr(),
4540 op.getAlignment(), op.getIsVolatile());
4541
4542 mlir::Type resultTy = getTypeConverter()->convertType(op.getType());
4543
4544 if (info.getIsSigned()) {
4545 assert(size <= storageSize);
4546 unsigned highBits = storageSize - size;
4547
4548 if (highBits) {
4549 resultVal = createShL(rewriter, resultVal, highBits);
4550 resultVal = createAShR(rewriter, resultVal, highBits);
4551 }
4552 }
4553
4554 resultVal = createIntCast(rewriter, resultVal,
4555 mlir::cast<mlir::IntegerType>(resultTy),
4556 info.getIsSigned());
4557
4558 rewriter.replaceOp(op, resultVal);
4559 return mlir::success();
4560}
4561
4562mlir::LogicalResult CIRToLLVMComplexImagPtrOpLowering::matchAndRewrite(
4563 cir::ComplexImagPtrOp op, OpAdaptor adaptor,
4564 mlir::ConversionPatternRewriter &rewriter) const {
4565 cir::PointerType operandTy = op.getOperand().getType();
4566 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4567 mlir::Type elementLLVMTy =
4568 getTypeConverter()->convertType(operandTy.getPointee());
4569
4570 mlir::LLVM::GEPArg gepIndices[2] = {{0}, {1}};
4571 mlir::LLVM::GEPNoWrapFlags inboundsNuw =
4572 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
4573 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4574 op, resultLLVMTy, elementLLVMTy, adaptor.getOperand(), gepIndices,
4575 inboundsNuw);
4576 return mlir::success();
4577}
4578
4579mlir::LogicalResult CIRToLLVMComplexRealPtrOpLowering::matchAndRewrite(
4580 cir::ComplexRealPtrOp op, OpAdaptor adaptor,
4581 mlir::ConversionPatternRewriter &rewriter) const {
4582 cir::PointerType operandTy = op.getOperand().getType();
4583 mlir::Type resultLLVMTy = getTypeConverter()->convertType(op.getType());
4584 mlir::Type elementLLVMTy =
4585 getTypeConverter()->convertType(operandTy.getPointee());
4586
4587 mlir::LLVM::GEPArg gepIndices[2] = {0, 0};
4588 mlir::LLVM::GEPNoWrapFlags inboundsNuw =
4589 mlir::LLVM::GEPNoWrapFlags::inbounds | mlir::LLVM::GEPNoWrapFlags::nuw;
4590 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(
4591 op, resultLLVMTy, elementLLVMTy, adaptor.getOperand(), gepIndices,
4592 inboundsNuw);
4593 return mlir::success();
4594}
4595
4596mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
4597 cir::GetBitfieldOp op, OpAdaptor adaptor,
4598 mlir::ConversionPatternRewriter &rewriter) const {
4599
4600 mlir::OpBuilder::InsertionGuard guard(rewriter);
4601 rewriter.setInsertionPoint(op);
4602
4603 cir::BitfieldInfoAttr info = op.getBitfieldInfo();
4604 uint64_t size = info.getSize();
4605 uint64_t offset = info.getOffset();
4606 mlir::Type storageType = info.getStorageType();
4607 mlir::MLIRContext *context = storageType.getContext();
4608 unsigned storageSize = 0;
4609
4610 mlir::IntegerType intType =
4611 computeBitfieldIntType(storageType, context, storageSize);
4612
4613 mlir::Value val = mlir::LLVM::LoadOp::create(
4614 rewriter, op.getLoc(), intType, adaptor.getAddr(), op.getAlignment(),
4615 op.getIsVolatile());
4616 val = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), intType, val);
4617
4618 if (info.getIsSigned()) {
4619 assert(static_cast<unsigned>(offset + size) <= storageSize);
4620 unsigned highBits = storageSize - offset - size;
4621 val = createShL(rewriter, val, highBits);
4622 val = createAShR(rewriter, val, offset + highBits);
4623 } else {
4624 val = createLShR(rewriter, val, offset);
4625
4626 if (static_cast<unsigned>(offset) + size < storageSize)
4627 val = createAnd(rewriter, val,
4628 llvm::APInt::getLowBitsSet(storageSize, size));
4629 }
4630
4631 mlir::Type resTy = getTypeConverter()->convertType(op.getType());
4632 mlir::Value newOp = createIntCast(
4633 rewriter, val, mlir::cast<mlir::IntegerType>(resTy), info.getIsSigned());
4634 rewriter.replaceOp(op, newOp);
4635 return mlir::success();
4636}
4637
4638mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite(
4639 cir::InlineAsmOp op, OpAdaptor adaptor,
4640 mlir::ConversionPatternRewriter &rewriter) const {
4641 mlir::Type llResTy;
4642 if (op.getNumResults())
4643 llResTy = getTypeConverter()->convertType(op.getType(0));
4644
4645 cir::AsmFlavor dialect = op.getAsmFlavor();
4646 mlir::LLVM::AsmDialect llDialect = dialect == cir::AsmFlavor::x86_att
4647 ? mlir::LLVM::AsmDialect::AD_ATT
4648 : mlir::LLVM::AsmDialect::AD_Intel;
4649
4651 StringRef llvmAttrName = mlir::LLVM::InlineAsmOp::getElementTypeAttrName();
4652
4653 // this is for the lowering to LLVM from LLVM dialect. Otherwise, if we
4654 // don't have the result (i.e. void type as a result of operation), the
4655 // element type attribute will be attached to the whole instruction, but not
4656 // to the operand
4657 if (!op.getNumResults())
4658 opAttrs.push_back(mlir::Attribute());
4659
4660 SmallVector<mlir::Value> llvmOperands;
4661 SmallVector<mlir::Value> cirOperands;
4662 for (auto const &[llvmOp, cirOp] :
4663 zip(adaptor.getAsmOperands(), op.getAsmOperands())) {
4664 append_range(llvmOperands, llvmOp);
4665 append_range(cirOperands, cirOp);
4666 }
4667
4668 // so far we infer the llvm dialect element type attr from
4669 // CIR operand type.
4670 for (auto const &[cirOpAttr, cirOp] :
4671 zip(op.getOperandAttrs(), cirOperands)) {
4672 if (!mlir::isa<mlir::UnitAttr>(cirOpAttr)) {
4673 opAttrs.push_back(mlir::Attribute());
4674 continue;
4675 }
4676
4678 cir::PointerType typ = mlir::cast<cir::PointerType>(cirOp.getType());
4679 mlir::TypeAttr typAttr = mlir::TypeAttr::get(convertTypeForMemory(
4680 *getTypeConverter(), dataLayout, typ.getPointee()));
4681
4682 attrs.push_back(rewriter.getNamedAttr(llvmAttrName, typAttr));
4683 mlir::DictionaryAttr newDict = rewriter.getDictionaryAttr(attrs);
4684 opAttrs.push_back(newDict);
4685 }
4686
4687 rewriter.replaceOpWithNewOp<mlir::LLVM::InlineAsmOp>(
4688 op, llResTy, llvmOperands, op.getAsmStringAttr(), op.getConstraintsAttr(),
4689 op.getSideEffectsAttr(),
4690 /*is_align_stack*/ mlir::UnitAttr(),
4691 /*tail_call_kind*/
4692 mlir::LLVM::TailCallKindAttr::get(
4693 getContext(), mlir::LLVM::tailcallkind::TailCallKind::None),
4694 mlir::LLVM::AsmDialectAttr::get(getContext(), llDialect),
4695 rewriter.getArrayAttr(opAttrs));
4696
4697 return mlir::success();
4698}
4699
4700mlir::LogicalResult CIRToLLVMVAStartOpLowering::matchAndRewrite(
4701 cir::VAStartOp op, OpAdaptor adaptor,
4702 mlir::ConversionPatternRewriter &rewriter) const {
4703 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
4704 auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
4705 adaptor.getArgList());
4706 rewriter.replaceOpWithNewOp<mlir::LLVM::VaStartOp>(op, vaList);
4707 return mlir::success();
4708}
4709
4710mlir::LogicalResult CIRToLLVMVAEndOpLowering::matchAndRewrite(
4711 cir::VAEndOp op, OpAdaptor adaptor,
4712 mlir::ConversionPatternRewriter &rewriter) const {
4713 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
4714 auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
4715 adaptor.getArgList());
4716 rewriter.replaceOpWithNewOp<mlir::LLVM::VaEndOp>(op, vaList);
4717 return mlir::success();
4718}
4719
4720mlir::LogicalResult CIRToLLVMVACopyOpLowering::matchAndRewrite(
4721 cir::VACopyOp op, OpAdaptor adaptor,
4722 mlir::ConversionPatternRewriter &rewriter) const {
4723 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
4724 auto dstList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
4725 adaptor.getDstList());
4726 auto srcList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
4727 adaptor.getSrcList());
4728 rewriter.replaceOpWithNewOp<mlir::LLVM::VaCopyOp>(op, dstList, srcList);
4729 return mlir::success();
4730}
4731
4732mlir::LogicalResult CIRToLLVMVAArgOpLowering::matchAndRewrite(
4733 cir::VAArgOp op, OpAdaptor adaptor,
4734 mlir::ConversionPatternRewriter &rewriter) const {
4736 auto opaquePtr = mlir::LLVM::LLVMPointerType::get(getContext());
4737 auto vaList = mlir::LLVM::BitcastOp::create(rewriter, op.getLoc(), opaquePtr,
4738 adaptor.getArgList());
4739
4740 mlir::Type llvmType =
4741 getTypeConverter()->convertType(op->getResultTypes().front());
4742 if (!llvmType)
4743 return mlir::failure();
4744
4745 rewriter.replaceOpWithNewOp<mlir::LLVM::VaArgOp>(op, llvmType, vaList);
4746 return mlir::success();
4747}
4748
4749mlir::LogicalResult CIRToLLVMLabelOpLowering::matchAndRewrite(
4750 cir::LabelOp op, OpAdaptor adaptor,
4751 mlir::ConversionPatternRewriter &rewriter) const {
4752 mlir::MLIRContext *ctx = rewriter.getContext();
4753 mlir::Block *block = op->getBlock();
4754 // A BlockTagOp cannot reside in the entry block. The address of the entry
4755 // block cannot be taken
4756 if (block->isEntryBlock()) {
4757 mlir::Block *newBlock =
4758 rewriter.splitBlock(op->getBlock(), mlir::Block::iterator(op));
4759 rewriter.setInsertionPointToEnd(block);
4760 mlir::LLVM::BrOp::create(rewriter, op.getLoc(), newBlock);
4761 }
4762 auto tagAttr =
4763 mlir::LLVM::BlockTagAttr::get(ctx, blockInfoAddr.getTagIndex());
4764 rewriter.setInsertionPoint(op);
4765
4766 auto blockTagOp =
4767 mlir::LLVM::BlockTagOp::create(rewriter, op->getLoc(), tagAttr);
4768 mlir::LLVM::LLVMFuncOp func = op->getParentOfType<mlir::LLVM::LLVMFuncOp>();
4769 auto blockInfoAttr =
4770 cir::BlockAddrInfoAttr::get(ctx, func.getSymName(), op.getLabel());
4771 blockInfoAddr.mapBlockTag(blockInfoAttr, blockTagOp);
4772 rewriter.eraseOp(op);
4773
4774 return mlir::success();
4775}
4776
4777mlir::LogicalResult CIRToLLVMBlockAddressOpLowering::matchAndRewrite(
4778 cir::BlockAddressOp op, OpAdaptor adaptor,
4779 mlir::ConversionPatternRewriter &rewriter) const {
4780 mlir::MLIRContext *ctx = rewriter.getContext();
4781
4782 mlir::LLVM::BlockTagOp matchLabel =
4783 blockInfoAddr.lookupBlockTag(op.getBlockAddrInfoAttr());
4784 mlir::LLVM::BlockTagAttr tagAttr;
4785 if (!matchLabel)
4786 // If the BlockTagOp has not been emitted yet, use a placeholder.
4787 // This will later be replaced with the correct tag index during
4788 // `resolveBlockAddressOp`.
4789 tagAttr = {};
4790 else
4791 tagAttr = matchLabel.getTag();
4792
4793 auto blkAddr = mlir::LLVM::BlockAddressAttr::get(
4794 rewriter.getContext(), op.getBlockAddrInfoAttr().getFunc(), tagAttr);
4795 rewriter.setInsertionPoint(op);
4796 auto newOp = mlir::LLVM::BlockAddressOp::create(
4797 rewriter, op.getLoc(), mlir::LLVM::LLVMPointerType::get(ctx), blkAddr);
4798 if (!matchLabel)
4799 blockInfoAddr.addUnresolvedBlockAddress(newOp, op.getBlockAddrInfoAttr());
4800 rewriter.replaceOp(op, newOp);
4801 return mlir::success();
4802}
4803
4804mlir::LogicalResult CIRToLLVMIndirectBrOpLowering::matchAndRewrite(
4805 cir::IndirectBrOp op, OpAdaptor adaptor,
4806 mlir::ConversionPatternRewriter &rewriter) const {
4807
4808 mlir::Value targetAddr = adaptor.getAddr();
4809
4810 // If the poison attribute is set, use llvm.mlir.poison as the address.
4811 // This happens when the block has no predecessors and is essentially
4812 // unreachable. Do NOT erase the block argument directly, as that violates
4813 // the MLIR dialect conversion framework contract (the framework tracks block
4814 // arguments and will clean them up). A block with no predecessors simply
4815 // produces no PHI node.
4816 if (op.getPoison()) {
4817 auto llvmPtrType = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4818 targetAddr =
4819 mlir::LLVM::PoisonOp::create(rewriter, op->getLoc(), llvmPtrType);
4820 }
4821
4822 rewriter.replaceOpWithNewOp<mlir::LLVM::IndirectBrOp>(
4823 op, targetAddr, adaptor.getSuccOperands(), op.getSuccessors());
4824 return mlir::success();
4825}
4826
4827mlir::LogicalResult CIRToLLVMAwaitOpLowering::matchAndRewrite(
4828 cir::AwaitOp op, OpAdaptor adaptor,
4829 mlir::ConversionPatternRewriter &rewriter) const {
4830 return mlir::failure();
4831}
4832
4833mlir::LogicalResult CIRToLLVMCpuIdOpLowering::matchAndRewrite(
4834 cir::CpuIdOp op, OpAdaptor adaptor,
4835 mlir::ConversionPatternRewriter &rewriter) const {
4836 mlir::Type i32Ty = rewriter.getI32Type();
4837 mlir::Type i64Ty = rewriter.getI64Type();
4838 mlir::Type i32PtrTy = mlir::LLVM::LLVMPointerType::get(i32Ty.getContext(), 0);
4839
4840 mlir::Type cpuidRetTy = mlir::LLVM::LLVMStructType::getLiteral(
4841 rewriter.getContext(), {i32Ty, i32Ty, i32Ty, i32Ty});
4842
4843 mlir::Value functionId = adaptor.getFunctionId();
4844 mlir::Value subFunctionId = adaptor.getSubFunctionId();
4845
4846 StringRef asmString, constraints;
4847 mlir::ModuleOp moduleOp = op->getParentOfType<mlir::ModuleOp>();
4848 llvm::Triple triple(
4849 mlir::cast<mlir::StringAttr>(
4850 moduleOp->getAttr(cir::CIRDialect::getTripleAttrName()))
4851 .getValue());
4852 if (triple.getArch() == llvm::Triple::x86) {
4853 asmString = "cpuid";
4854 constraints = "={ax},={bx},={cx},={dx},{ax},{cx}";
4855 } else {
4856 // x86-64 uses %rbx as the base register, so preserve it.
4857 asmString = "xchgq %rbx, ${1:q}\n"
4858 "cpuid\n"
4859 "xchgq %rbx, ${1:q}";
4860 constraints = "={ax},=r,={cx},={dx},0,2";
4861 }
4862
4863 mlir::Value inlineAsm =
4864 mlir::LLVM::InlineAsmOp::create(
4865 rewriter, op.getLoc(), cpuidRetTy, {functionId, subFunctionId},
4866 rewriter.getStringAttr(asmString),
4867 rewriter.getStringAttr(constraints),
4868 /*has_side_effects=*/mlir::UnitAttr{},
4869 /*is_align_stack=*/mlir::UnitAttr{},
4870 /*tail_call_kind=*/mlir::LLVM::TailCallKindAttr{},
4871 /*asm_dialect=*/mlir::LLVM::AsmDialectAttr{},
4872 /*operand_attrs=*/mlir::ArrayAttr{})
4873 .getResult(0);
4874
4875 mlir::Value basePtr = adaptor.getCpuInfo();
4876
4877 mlir::DataLayout layout(op->getParentOfType<mlir::ModuleOp>());
4878 unsigned alignment = layout.getTypeABIAlignment(i32Ty);
4879 for (unsigned i = 0; i < 4; i++) {
4880 mlir::Value extracted =
4881 mlir::LLVM::ExtractValueOp::create(rewriter, op.getLoc(), inlineAsm, i)
4882 .getResult();
4883 mlir::Value index = mlir::LLVM::ConstantOp::create(
4884 rewriter, op.getLoc(), i64Ty, rewriter.getI64IntegerAttr(i));
4885 llvm::SmallVector<mlir::Value, 1> gepIndices = {index};
4886 mlir::Value storePtr = mlir::LLVM::GEPOp::create(
4887 rewriter, op.getLoc(), i32PtrTy, i32Ty, basePtr,
4888 gepIndices, mlir::LLVM::GEPNoWrapFlags::none)
4889 .getResult();
4890 mlir::LLVM::StoreOp::create(rewriter, op.getLoc(), extracted, storePtr,
4891 alignment);
4892 }
4893
4894 rewriter.eraseOp(op);
4895 return mlir::success();
4896}
4897
4898mlir::LogicalResult CIRToLLVMMemChrOpLowering::matchAndRewrite(
4899 cir::MemChrOp op, OpAdaptor adaptor,
4900 mlir::ConversionPatternRewriter &rewriter) const {
4901 auto llvmPtrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext());
4902 mlir::Type srcTy = getTypeConverter()->convertType(op.getSrc().getType());
4903 mlir::Type patternTy =
4904 getTypeConverter()->convertType(op.getPattern().getType());
4905 mlir::Type lenTy = getTypeConverter()->convertType(op.getLen().getType());
4906 auto fnTy =
4907 mlir::LLVM::LLVMFunctionType::get(llvmPtrTy, {srcTy, patternTy, lenTy},
4908 /*isVarArg=*/false);
4909 llvm::StringRef fnName = "memchr";
4910
4911 mlir::Builder b(rewriter.getContext());
4912 mlir::NamedAttribute noundefAttr =
4913 b.getNamedAttr("llvm.noundef", b.getUnitAttr());
4914 mlir::DictionaryAttr noundefDict = mlir::DictionaryAttr::get(
4915 rewriter.getContext(), llvm::ArrayRef(noundefAttr));
4916 SmallVector<mlir::Attribute> argAttrVec(3, noundefDict);
4917 mlir::ArrayAttr argAttrs =
4918 mlir::ArrayAttr::get(rewriter.getContext(), argAttrVec);
4919
4920 createLLVMFuncOpIfNotExist(rewriter, op, fnName, fnTy, argAttrs);
4921
4922 mlir::LLVM::CallOp newCall = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
4923 op, mlir::TypeRange{llvmPtrTy}, fnName,
4924 mlir::ValueRange{adaptor.getSrc(), adaptor.getPattern(),
4925 adaptor.getLen()});
4926 newCall.setArgAttrsAttr(argAttrs);
4927 return mlir::success();
4928}
4929
4930std::unique_ptr<mlir::Pass> createConvertCIRToLLVMPass() {
4931 return std::make_unique<ConvertCIRToLLVMPass>();
4932}
4933
4934void populateCIRToLLVMPasses(mlir::OpPassManager &pm) {
4936 pm.addPass(mlir::omp::createMarkDeclareTargetPass());
4937 pm.addPass(createConvertCIRToLLVMPass());
4938}
4939
4940std::unique_ptr<llvm::Module>
4941lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, LLVMContext &llvmCtx,
4942 StringRef mlirSaveTempsOutFile,
4943 llvm::vfs::FileSystem *fs) {
4944 llvm::TimeTraceScope scope("lower from CIR to LLVM directly");
4945
4946 mlir::MLIRContext *mlirCtx = mlirModule.getContext();
4947
4948 mlir::PassManager pm(mlirCtx);
4950
4951 (void)mlir::applyPassManagerCLOptions(pm);
4952
4953 if (mlir::failed(pm.run(mlirModule))) {
4954 // FIXME: Handle any errors where they occurs and return a nullptr here.
4955 report_fatal_error(
4956 "The pass manager failed to lower CIR to LLVMIR dialect!");
4957 }
4958
4959 if (!mlirSaveTempsOutFile.empty()) {
4960 std::error_code ec;
4961 llvm::raw_fd_ostream out(mlirSaveTempsOutFile, ec);
4962 if (!ec)
4963 mlirModule->print(out);
4964 }
4965
4966 mlir::registerBuiltinDialectTranslation(*mlirCtx);
4967 mlir::registerLLVMDialectTranslation(*mlirCtx);
4968 mlir::registerOpenMPDialectTranslation(*mlirCtx);
4970
4971 llvm::TimeTraceScope translateScope("translateModuleToLLVMIR");
4972
4973 StringRef moduleName = mlirModule.getName().value_or("CIRToLLVMModule");
4974 std::unique_ptr<llvm::Module> llvmModule = mlir::translateModuleToLLVMIR(
4975 mlirModule, llvmCtx, moduleName, /*disableVerification=*/false, fs);
4976
4977 if (!llvmModule) {
4978 // FIXME: Handle any errors where they occurs and return a nullptr here.
4979 report_fatal_error("Lowering from LLVMIR dialect to llvm IR failed!");
4980 }
4981
4982 return llvmModule;
4983}
4984} // namespace direct
4985} // 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)
mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs)
mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs, const llvm::APInt &rhs)
std::optional< mlir::Attribute > lowerConstArrayAttr(cir::ConstArrayAttr constArr, const mlir::TypeConverter *converter, mlir::ModuleOp moduleOp={})
*collection of selector each with an associated kind and an ordered *collection of selectors A selector has a kind
static bool isVector(QualType QT, QualType ElementType)
This helper function returns true if QT is a vector type that has element type ElementType.
__DEVICE__ void * memset(void *__a, int __b, size_t __c)
CIRAttrToValue(mlir::Operation *parentOp, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *converter)
mlir::Attribute visit(mlir::Attribute attr)
mlir::Attribute visitCirAttr(cir::FPAttr attr)
mlir::Attribute visitCirAttr(cir::BoolAttr attr)
GlobalInitAttrRewriter(mlir::Type type, mlir::ConversionPatternRewriter &rewriter)
mlir::Attribute visitCirAttr(cir::IntAttr attr)
static mlir::LLVM::CConv convertCallingConv(cir::CallingConv callingConv)
static mlir::LogicalResult lowerIncDecOp(CIROp op, typename CIROp::Adaptor adaptor, mlir::ConversionPatternRewriter &rewriter)
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.
void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow, cir::SideEffect sideEffect, mlir::LLVM::MemoryEffectsAttr &memoryEffect, bool &noUnwind, bool &willReturn, bool &noReturn)
static bool isBulkLowerableConstArrayBaseElement(mlir::Type baseElemTy)
static mlir::LLVM::IntegerOverflowFlags intOverflowFlag(BinOp op)
void createLLVMFuncOpIfNotExist(mlir::ConversionPatternRewriter &rewriter, mlir::Operation *srcOp, llvm::StringRef fnName, mlir::Type fnTy, mlir::ArrayAttr argAttrs=nullptr, mlir::ArrayAttr resAttrs=nullptr)
static mlir::Value getLLVMIntCast(mlir::ConversionPatternRewriter &rewriter, mlir::Value llvmSrc, mlir::Type llvmDstIntTy, bool isUnsigned, uint64_t cirSrcWidth, uint64_t cirDstIntWidth)
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)
static mlir::LogicalResult lowerMinMaxOp(CIROp op, typename CIROp::Adaptor adaptor, mlir::ConversionPatternRewriter &rewriter)
static mlir::LLVM::LLVMStructType getLLVMLandingPadStructTy(mlir::ConversionPatternRewriter &rewriter)
mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, const mlir::Attribute attr, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *converter)
Switches on the type of attribute and calls the appropriate conversion.
static mlir::Value convertToIndexTy(mlir::ConversionPatternRewriter &rewriter, mlir::ModuleOp mod, mlir::Value index, mlir::Type baseTy, cir::IntType strideTy)
static mlir::LogicalResult lowerIntBinaryOp(CIROp op, mlir::Value lhs, mlir::Value rhs, mlir::ConversionPatternRewriter &rewriter)
Lower an integer Div/Rem op to its signed or unsigned LLVM counterpart.
static mlir::LLVM::CallIntrinsicOp replaceOpWithCallLLVMIntrinsicOp(mlir::ConversionPatternRewriter &rewriter, mlir::Operation *op, const llvm::Twine &intrinsicName, mlir::Type resultTy, mlir::ValueRange operands)
static void prepareTypeConverter(mlir::LLVMTypeConverter &converter, mlir::DataLayout &dataLayout)
static mlir::LLVM::AtomicOrdering getLLVMMemOrder(std::optional< cir::MemOrder > memorder)
std::unique_ptr< mlir::Pass > createConvertCIRToLLVMPass()
Create a pass that fully lowers CIR to the LLVMIR dialect.
static llvm::StringRef getLLVMSyncScope(cir::SyncScopeKind syncScope)
static mlir::LogicalResult lowerSaturatableArithOp(CIROp op, mlir::Value lhs, mlir::Value rhs, mlir::ConversionPatternRewriter &rewriter)
Lower an arithmetic op that supports saturation, overflow flags, and an FP Lower an integer Add/Sub o...
static mlir::Type getConstArrayBaseElementType(mlir::Type ty)
static mlir::LLVM::FCmpPredicate convertCmpKindToFCmpPredicate(cir::CmpOpKind kind)
Convert from a CIR comparison kind to an LLVM IR floating-point comparison kind.
static mlir::LogicalResult rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *converter, mlir::SymbolTableCollection &symbolTables, mlir::FlatSymbolRefAttr calleeAttr, mlir::Block *continueBlock=nullptr, mlir::Block *landingPadBlock=nullptr)
static mlir::LLVM::Visibility lowerCIRVisibilityToLLVMVisibility(cir::VisibilityKind visibilityKind)
static void lowerCallAttributes(cir::CIRCallOpInterface op, SmallVectorImpl< mlir::NamedAttribute > &result)
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.
static llvm::StringLiteral getLLVMBinopForPostAtomic(cir::AtomicFetchKind k, bool isInt)
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::LogicalResult lowerBinOpOverflow(OpTy op, typename OpTy::Adaptor adaptor, mlir::ConversionPatternRewriter &rewriter, const mlir::TypeConverter *typeConverter, llvm::StringRef opStr)
Shared lowering logic for checked binary arithmetic overflow operations.
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::LLVM::IntegerOverflowFlags nswFlag(bool nsw)
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.
std::unique_ptr< llvm::Module > lowerDirectlyFromCIRToLLVMIR(mlir::ModuleOp mlirModule, llvm::LLVMContext &llvmCtx, llvm::StringRef mlirSaveTempsOutFile={}, llvm::vfs::FileSystem *fs=nullptr)
static bool isIntTypeUnsigned(mlir::Type type)
void collectUnreachable(mlir::Operation *parent, llvm::SmallVectorImpl< mlir::Operation * > &ops)
Collect ops in blocks that are unreachable from their region's entry, appending them to ops.
llvm::Type * convertTypeForMemory(CodeGenModule &CGM, QualType T)
const internal::VariadicAllOfMatcher< Attr > attr
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
const AstTypeMatcher< ComplexType > complexType
RangeSelector name(std::string ID)
Given a node with a "name", (like NamedDecl, DeclRefExpr, CxxCtorInitializer, and TypeLoc) selects th...
@ Default
Set to the current date and time.
unsigned long uint64_t
long int64_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 ?
__packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 __packed_splat2 __packed_splat4 __packed_splat2 __packed_splat8 __packed_splat4 uint32_t
static bool dataMemberType()
static bool addressSpace()
static bool globalViewIntLowering()
static bool opAllocaAnnotations()
static bool opLoadStoreTbaa()
static bool optInfoAttr()
static bool opFuncExtraAttrs()
static bool isPPC_FP128Ty()
static bool vaArgABILowering()
static bool fpConstraints()
static bool intrinsicElementTypeSupport()
static bool lowerModeOptLevel()
static bool opCallCallConv()
static bool aggValueSlotVolatile()
static bool fastMathFlags()
static bool llvmLoweringPtrDiffConsidersPointee()
static bool opLoadStoreNontemporal()
static bool atomicSyncScopeID()
static bool opFuncMultipleReturnVals()
void collectGlobalAnnotations(mlir::ModuleOp module)
Collect (symbol_name, annotations, loc) from cir.func and cir.global ops before the conversion runs (...
StringRef getDescription() const override
StringRef getArgument() const override
void getDependentDialects(mlir::DialectRegistry &registry) const override
void resolveBlockAddressOp(LLVMBlockAddressInfo &blockInfoAddr)
void buildGlobalAnnotationsVar(mlir::ModuleOp module)
Emit @llvm.global.annotations and supporting string/args constants from the previously-collected anno...
void processCIRAttrs(mlir::ModuleOp module)
mlir::LLVM::BlockTagOp lookupBlockTag(cir::BlockAddrInfoAttr info) const
Definition LowerToLLVM.h:49
llvm::DenseMap< mlir::LLVM::BlockAddressOp, cir::BlockAddrInfoAttr > & getUnresolvedBlockAddress()
Definition LowerToLLVM.h:62