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