MLIR 24.0.0git
PtrToLLVM.cpp
Go to the documentation of this file.
1//===- PtrToLLVM.cpp - Ptr to LLVM dialect conversion ---------------------===//
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
10
21#include "llvm/Support/LogicalResult.h"
22#include <type_traits>
23
24using namespace mlir;
25
26namespace {
27//===----------------------------------------------------------------------===//
28// FromPtrOpConversion
29//===----------------------------------------------------------------------===//
30struct FromPtrOpConversion : public ConvertOpToLLVMPattern<ptr::FromPtrOp> {
32 LogicalResult
33 matchAndRewrite(ptr::FromPtrOp op, OpAdaptor adaptor,
34 ConversionPatternRewriter &rewriter) const override;
35};
36
37//===----------------------------------------------------------------------===//
38// GetMetadataOpConversion
39//===----------------------------------------------------------------------===//
40struct GetMetadataOpConversion
41 : public ConvertOpToLLVMPattern<ptr::GetMetadataOp> {
43 LogicalResult
44 matchAndRewrite(ptr::GetMetadataOp op, OpAdaptor adaptor,
45 ConversionPatternRewriter &rewriter) const override;
46};
47
48//===----------------------------------------------------------------------===//
49// PtrAddOpConversion
50//===----------------------------------------------------------------------===//
51struct PtrAddOpConversion : public ConvertOpToLLVMPattern<ptr::PtrAddOp> {
53 LogicalResult
54 matchAndRewrite(ptr::PtrAddOp op, OpAdaptor adaptor,
55 ConversionPatternRewriter &rewriter) const override;
56};
57
58//===----------------------------------------------------------------------===//
59// ToPtrOpConversion
60//===----------------------------------------------------------------------===//
61struct ToPtrOpConversion : public ConvertOpToLLVMPattern<ptr::ToPtrOp> {
63 LogicalResult
64 matchAndRewrite(ptr::ToPtrOp op, OpAdaptor adaptor,
65 ConversionPatternRewriter &rewriter) const override;
66};
67
68//===----------------------------------------------------------------------===//
69// TypeOffsetOpConversion
70//===----------------------------------------------------------------------===//
71struct TypeOffsetOpConversion
72 : public ConvertOpToLLVMPattern<ptr::TypeOffsetOp> {
74 LogicalResult
75 matchAndRewrite(ptr::TypeOffsetOp op, OpAdaptor adaptor,
76 ConversionPatternRewriter &rewriter) const override;
77};
78
79//===----------------------------------------------------------------------===//
80// ConstantOpConversion
81//===----------------------------------------------------------------------===//
82struct ConstantOpConversion : public ConvertOpToLLVMPattern<ptr::ConstantOp> {
84 LogicalResult
85 matchAndRewrite(ptr::ConstantOp op, OpAdaptor adaptor,
86 ConversionPatternRewriter &rewriter) const override;
87};
88} // namespace
89
90//===----------------------------------------------------------------------===//
91// Internal functions
92//===----------------------------------------------------------------------===//
93
94// Function to create an LLVM struct type representing a memref metadata.
95static FailureOr<LLVM::LLVMStructType>
97 const LLVMTypeConverter &typeConverter) {
98 MLIRContext *context = type.getContext();
99 // Get the address space.
100 FailureOr<unsigned> addressSpace = typeConverter.getMemRefAddressSpace(type);
101 if (failed(addressSpace))
102 return failure();
103
104 // Get pointer type (using address space 0 by default)
105 auto ptrType = LLVM::LLVMPointerType::get(context, *addressSpace);
106
107 // Get the strides offsets and shape.
108 SmallVector<int64_t> strides;
109 int64_t offset;
110 if (failed(type.getStridesAndOffset(strides, offset)))
111 return failure();
112 ArrayRef<int64_t> shape = type.getShape();
113
114 // Use index type from the type converter for the descriptor elements
115 Type indexType = typeConverter.getIndexType();
116
117 // For a ranked memref, the descriptor contains:
118 // 1. The pointer to the allocated data
119 // 2. The pointer to the aligned data
120 // 3. The dynamic offset?
121 // 4. The dynamic sizes?
122 // 5. The dynamic strides?
123 SmallVector<Type, 5> elements;
124
125 // Allocated pointer.
126 elements.push_back(ptrType);
127
128 // Potentially add the dynamic offset.
129 if (offset == ShapedType::kDynamic)
130 elements.push_back(indexType);
131
132 // Potentially add the dynamic sizes.
133 for (int64_t dim : shape) {
134 if (dim == ShapedType::kDynamic)
135 elements.push_back(indexType);
136 }
137
138 // Potentially add the dynamic strides.
139 for (int64_t stride : strides) {
140 if (stride == ShapedType::kDynamic)
141 elements.push_back(indexType);
142 }
143 return LLVM::LLVMStructType::getLiteral(context, elements);
144}
145
146//===----------------------------------------------------------------------===//
147// FromPtrOpConversion
148//===----------------------------------------------------------------------===//
149
150LogicalResult FromPtrOpConversion::matchAndRewrite(
151 ptr::FromPtrOp op, OpAdaptor adaptor,
152 ConversionPatternRewriter &rewriter) const {
153 // Get the target memref type
154 auto mTy = dyn_cast<MemRefType>(op.getResult().getType());
155 if (!mTy)
156 return rewriter.notifyMatchFailure(op, "Expected memref result type");
157
158 if (!op.getMetadata() && op.getType().hasPtrMetadata()) {
159 return rewriter.notifyMatchFailure(
160 op, "Can convert only memrefs with metadata");
161 }
162
163 // Convert the result type
164 Type descriptorTy = getTypeConverter()->convertType(mTy);
165 if (!descriptorTy)
166 return rewriter.notifyMatchFailure(op, "Failed to convert result type");
167
168 // Get the strides, offsets and shape.
169 SmallVector<int64_t> strides;
170 int64_t offset;
171 if (failed(mTy.getStridesAndOffset(strides, offset))) {
172 return rewriter.notifyMatchFailure(op,
173 "Failed to get the strides and offset");
174 }
175 ArrayRef<int64_t> shape = mTy.getShape();
176
177 // Create a new memref descriptor
178 Location loc = op.getLoc();
179 auto desc = MemRefDescriptor::poison(rewriter, loc, descriptorTy);
180
181 // Set the allocated and aligned pointers.
182 desc.setAllocatedPtr(
183 rewriter, loc,
184 LLVM::ExtractValueOp::create(rewriter, loc, adaptor.getMetadata(), 0));
185 desc.setAlignedPtr(rewriter, loc, adaptor.getPtr());
186
187 // Extract metadata from the passed struct.
188 unsigned fieldIdx = 1;
189
190 // Set dynamic offset if needed.
191 if (offset == ShapedType::kDynamic) {
192 Value offsetValue = LLVM::ExtractValueOp::create(
193 rewriter, loc, adaptor.getMetadata(), fieldIdx++);
194 desc.setOffset(rewriter, loc, offsetValue);
195 } else {
196 desc.setConstantOffset(rewriter, loc, offset);
197 }
198
199 // Set dynamic sizes if needed.
200 for (auto [i, dim] : llvm::enumerate(shape)) {
201 if (dim == ShapedType::kDynamic) {
202 Value sizeValue = LLVM::ExtractValueOp::create(
203 rewriter, loc, adaptor.getMetadata(), fieldIdx++);
204 desc.setSize(rewriter, loc, i, sizeValue);
205 } else {
206 desc.setConstantSize(rewriter, loc, i, dim);
207 }
208 }
209
210 // Set dynamic strides if needed.
211 for (auto [i, stride] : llvm::enumerate(strides)) {
212 if (stride == ShapedType::kDynamic) {
213 Value strideValue = LLVM::ExtractValueOp::create(
214 rewriter, loc, adaptor.getMetadata(), fieldIdx++);
215 desc.setStride(rewriter, loc, i, strideValue);
216 } else {
217 desc.setConstantStride(rewriter, loc, i, stride);
218 }
219 }
220
221 rewriter.replaceOp(op, static_cast<Value>(desc));
222 return success();
223}
224
225//===----------------------------------------------------------------------===//
226// GetMetadataOpConversion
227//===----------------------------------------------------------------------===//
228
229LogicalResult GetMetadataOpConversion::matchAndRewrite(
230 ptr::GetMetadataOp op, OpAdaptor adaptor,
231 ConversionPatternRewriter &rewriter) const {
232 auto mTy = dyn_cast<MemRefType>(op.getPtr().getType());
233 if (!mTy)
234 return rewriter.notifyMatchFailure(op, "Only memref metadata is supported");
235
236 // Get the metadata type.
237 FailureOr<LLVM::LLVMStructType> mdTy =
238 createMemRefMetadataType(mTy, *getTypeConverter());
239 if (failed(mdTy)) {
240 return rewriter.notifyMatchFailure(op,
241 "Failed to create the metadata type");
242 }
243
244 // Get the memref descriptor.
245 MemRefDescriptor descriptor(adaptor.getPtr());
246
247 // Get the strides offsets and shape.
248 SmallVector<int64_t> strides;
249 int64_t offset;
250 if (failed(mTy.getStridesAndOffset(strides, offset))) {
251 return rewriter.notifyMatchFailure(op,
252 "Failed to get the strides and offset");
253 }
254 ArrayRef<int64_t> shape = mTy.getShape();
255
256 // Create a new LLVM struct to hold the metadata
257 Location loc = op.getLoc();
258 Value sV = LLVM::UndefOp::create(rewriter, loc, *mdTy);
259
260 // First element is the allocated pointer.
261 SmallVector<int64_t> pos{0};
262 sV = LLVM::InsertValueOp::create(rewriter, loc, sV,
263 descriptor.allocatedPtr(rewriter, loc), pos);
264
265 // Track the current field index.
266 unsigned fieldIdx = 1;
267
268 // Add dynamic offset if needed.
269 if (offset == ShapedType::kDynamic) {
270 sV = LLVM::InsertValueOp::create(
271 rewriter, loc, sV, descriptor.offset(rewriter, loc), fieldIdx++);
272 }
273
274 // Add dynamic sizes if needed.
275 for (auto [i, dim] : llvm::enumerate(shape)) {
276 if (dim != ShapedType::kDynamic)
277 continue;
278 sV = LLVM::InsertValueOp::create(
279 rewriter, loc, sV, descriptor.size(rewriter, loc, i), fieldIdx++);
280 }
281
282 // Add dynamic strides if needed
283 for (auto [i, stride] : llvm::enumerate(strides)) {
284 if (stride != ShapedType::kDynamic)
285 continue;
286 sV = LLVM::InsertValueOp::create(
287 rewriter, loc, sV, descriptor.stride(rewriter, loc, i), fieldIdx++);
288 }
289 rewriter.replaceOp(op, sV);
290 return success();
291}
292
293//===----------------------------------------------------------------------===//
294// PtrAddOpConversion
295//===----------------------------------------------------------------------===//
296
297LogicalResult
298PtrAddOpConversion::matchAndRewrite(ptr::PtrAddOp op, OpAdaptor adaptor,
299 ConversionPatternRewriter &rewriter) const {
300 // Get and check the base.
301 Value base = adaptor.getBase();
302 if (!isa<LLVM::LLVMPointerType>(getElementTypeOrSelf(base.getType())))
303 return rewriter.notifyMatchFailure(op, "Incompatible pointer type");
304
305 // A vector offset can broadcast a scalar base to a vector result.
306 Type resultType = getTypeConverter()->convertType(op.getType());
307 if (!resultType ||
308 !isa<LLVM::LLVMPointerType>(getElementTypeOrSelf(resultType)))
309 return rewriter.notifyMatchFailure(op, "Incompatible result type");
310
311 // Get the offset.
312 Value offset = adaptor.getOffset();
313
314 // Ptr assumes the offset is in bytes.
315 Type elementType = IntegerType::get(rewriter.getContext(), 8);
316
317 // Convert the `ptradd` flags.
318 LLVM::GEPNoWrapFlags flags;
319 switch (op.getFlags()) {
320 case ptr::PtrAddFlags::none:
321 flags = LLVM::GEPNoWrapFlags::none;
322 break;
323 case ptr::PtrAddFlags::nusw:
324 flags = LLVM::GEPNoWrapFlags::nusw;
325 break;
326 case ptr::PtrAddFlags::nuw:
327 flags = LLVM::GEPNoWrapFlags::nuw;
328 break;
329 case ptr::PtrAddFlags::inbounds:
330 flags = LLVM::GEPNoWrapFlags::inbounds;
331 break;
332 }
333
334 // Create the GEP operation with appropriate arguments
335 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, resultType, elementType, base,
336 ValueRange{offset}, flags);
337 return success();
338}
339
340//===----------------------------------------------------------------------===//
341// ToPtrOpConversion
342//===----------------------------------------------------------------------===//
343
344LogicalResult
345ToPtrOpConversion::matchAndRewrite(ptr::ToPtrOp op, OpAdaptor adaptor,
346 ConversionPatternRewriter &rewriter) const {
347 // Bail if it's not a memref.
348 if (!isa<MemRefType>(op.getPtr().getType()))
349 return rewriter.notifyMatchFailure(op, "Expected a memref input");
350
351 // Extract the aligned pointer from the memref descriptor.
352 rewriter.replaceOp(
353 op, MemRefDescriptor(adaptor.getPtr()).alignedPtr(rewriter, op.getLoc()));
354 return success();
355}
356
357//===----------------------------------------------------------------------===//
358// TypeOffsetOpConversion
359//===----------------------------------------------------------------------===//
360
361LogicalResult TypeOffsetOpConversion::matchAndRewrite(
362 ptr::TypeOffsetOp op, OpAdaptor adaptor,
363 ConversionPatternRewriter &rewriter) const {
364 // Convert the type attribute.
365 Type type = getTypeConverter()->convertType(op.getElementType());
366 if (!type)
367 return rewriter.notifyMatchFailure(op, "Couldn't convert the type");
368
369 // Convert the result type.
370 Type rTy = getTypeConverter()->convertType(op.getResult().getType());
371 if (!rTy)
372 return rewriter.notifyMatchFailure(op, "Couldn't convert the result type");
373
374 // TODO: Use MLIR's data layout. We don't use it because overall support is
375 // still flaky.
376
377 // Create an LLVM pointer type for the GEP operation.
378 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
379
380 // Create a GEP operation to compute the offset of the type.
381 auto offset =
382 LLVM::GEPOp::create(rewriter, op.getLoc(), ptrTy, type,
383 LLVM::ZeroOp::create(rewriter, op.getLoc(), ptrTy),
384 ArrayRef<LLVM::GEPArg>({LLVM::GEPArg(1)}));
385
386 // Replace the original op with a PtrToIntOp using the computed offset.
387 rewriter.replaceOpWithNewOp<LLVM::PtrToIntOp>(op, rTy, offset.getRes());
388 return success();
389}
390
391//===----------------------------------------------------------------------===//
392// ConstantOpConversion
393//===----------------------------------------------------------------------===//
394
395LogicalResult ConstantOpConversion::matchAndRewrite(
396 ptr::ConstantOp op, OpAdaptor adaptor,
397 ConversionPatternRewriter &rewriter) const {
398 TypedAttr value = op.getValue();
399 Type resultType = getTypeConverter()->convertType(op.getType());
400 if (!resultType)
401 return rewriter.notifyMatchFailure(op, "Couldn't convert the result type");
402
403 if (isa<ptr::NullAttr>(value)) {
404 rewriter.replaceOpWithNewOp<LLVM::ZeroOp>(op, resultType);
405 return success();
406 }
407 auto addrAttr = dyn_cast<ptr::AddressAttr>(value);
408 // Early-exit if unknown attribute.
409 if (!addrAttr) {
410 return rewriter.notifyMatchFailure(
411 op, "unsupported value attribute kind: " +
412 value.getAbstractAttribute().getName());
413 }
414 Type intType = rewriter.getIntegerType(addrAttr.getValue().getBitWidth());
415 Value intConst = LLVM::ConstantOp::create(rewriter, op.getLoc(), intType,
416 addrAttr.getValue());
417 rewriter.replaceOpWithNewOp<LLVM::IntToPtrOp>(op, resultType, intConst);
418 return success();
419}
420
421//===----------------------------------------------------------------------===//
422// ConvertToLLVMPatternInterface implementation
423//===----------------------------------------------------------------------===//
424
425namespace {
426/// Implement the interface to convert Ptr to LLVM.
427struct PtrToLLVMDialectInterface : public ConvertToLLVMPatternInterface {
428 PtrToLLVMDialectInterface(Dialect *dialect)
429 : ConvertToLLVMPatternInterface(dialect) {}
430
431 void loadDependentDialects(MLIRContext *context) const final {
432 context->loadDialect<LLVM::LLVMDialect>();
433 }
434
435 /// Hook for derived dialect interface to provide conversion patterns
436 /// and mark dialect legal for the conversion target.
437 void populateConvertToLLVMConversionPatterns(
438 ConversionTarget &target, LLVMTypeConverter &converter,
439 RewritePatternSet &patterns) const final {
440 ptr::populatePtrToLLVMConversionPatterns(converter, patterns);
441 }
442};
443} // namespace
444
445//===----------------------------------------------------------------------===//
446// API
447//===----------------------------------------------------------------------===//
448
450 LLVMTypeConverter &converter, RewritePatternSet &patterns) {
451 // Add address space conversions.
452 converter.addTypeAttributeConversion(
453 [&](PtrLikeTypeInterface type, ptr::GenericSpaceAttr memorySpace)
454 -> TypeConverter::AttributeConversionResult {
455 if (type.getMemorySpace() != memorySpace)
456 return TypeConverter::AttributeConversionResult::na();
457 return IntegerAttr::get(IntegerType::get(type.getContext(), 32), 0);
458 });
459
460 // Add type conversions.
461 converter.addConversion([&](ptr::PtrType type) -> Type {
462 std::optional<Attribute> maybeAttr =
463 converter.convertTypeAttribute(type, type.getMemorySpace());
464 auto memSpace =
465 maybeAttr ? dyn_cast_or_null<IntegerAttr>(*maybeAttr) : IntegerAttr();
466 if (!memSpace)
467 return {};
468 return LLVM::LLVMPointerType::get(type.getContext(),
469 memSpace.getValue().getSExtValue());
470 });
471
472 // Convert ptr metadata of memref type.
473 converter.addConversion([&](ptr::PtrMetadataType type) -> Type {
474 auto mTy = dyn_cast<MemRefType>(type.getType());
475 if (!mTy)
476 return {};
477 FailureOr<LLVM::LLVMStructType> res =
478 createMemRefMetadataType(mTy, converter);
479 return failed(res) ? Type() : res.value();
480 });
481
482 // Add conversion patterns.
483 patterns.add<FromPtrOpConversion, GetMetadataOpConversion, PtrAddOpConversion,
484 ToPtrOpConversion, TypeOffsetOpConversion, ConstantOpConversion>(
485 converter);
486}
487
489 registry.addExtension(+[](MLIRContext *ctx, ptr::PtrDialect *dialect) {
490 dialect->addInterfaces<PtrToLLVMDialectInterface>();
491 });
492}
return success()
b getContext())
static FailureOr< LLVM::LLVMStructType > createMemRefMetadataType(MemRefType type, const LLVMTypeConverter &typeConverter)
Definition PtrToLLVM.cpp:96
Utility class for operation conversions targeting the LLVM dialect that match exactly one source oper...
Definition Pattern.h:233
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:239
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
Conversion from types to the LLVM IR dialect.
FailureOr< unsigned > getMemRefAddressSpace(BaseMemRefType type) const
Return the LLVM address space corresponding to the memory space of the memref type type or failure if...
Type getIndexType() const
Gets the LLVM representation of the index type.
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:64
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
Type getType() const
Return the type of this value.
Definition Value.h:105
void populatePtrToLLVMConversionPatterns(LLVMTypeConverter &converter, RewritePatternSet &patterns)
Populate the convert to LLVM patterns for the ptr dialect.
void registerConvertPtrToLLVMInterface(DialectRegistry &registry)
Register the convert to LLVM interface for the ptr dialect.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.
Type getElementTypeOrSelf(Type type)
Return the element type or return the type itself.