MLIR 23.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>(base.getType()))
303 return rewriter.notifyMatchFailure(op, "Incompatible pointer type");
304
305 // Get the offset.
306 Value offset = adaptor.getOffset();
307
308 // Ptr assumes the offset is in bytes.
309 Type elementType = IntegerType::get(rewriter.getContext(), 8);
310
311 // Convert the `ptradd` flags.
312 LLVM::GEPNoWrapFlags flags;
313 switch (op.getFlags()) {
314 case ptr::PtrAddFlags::none:
315 flags = LLVM::GEPNoWrapFlags::none;
316 break;
317 case ptr::PtrAddFlags::nusw:
318 flags = LLVM::GEPNoWrapFlags::nusw;
319 break;
320 case ptr::PtrAddFlags::nuw:
321 flags = LLVM::GEPNoWrapFlags::nuw;
322 break;
323 case ptr::PtrAddFlags::inbounds:
324 flags = LLVM::GEPNoWrapFlags::inbounds;
325 break;
326 }
327
328 // Create the GEP operation with appropriate arguments
329 rewriter.replaceOpWithNewOp<LLVM::GEPOp>(op, base.getType(), elementType,
330 base, ValueRange{offset}, flags);
331 return success();
332}
333
334//===----------------------------------------------------------------------===//
335// ToPtrOpConversion
336//===----------------------------------------------------------------------===//
337
338LogicalResult
339ToPtrOpConversion::matchAndRewrite(ptr::ToPtrOp op, OpAdaptor adaptor,
340 ConversionPatternRewriter &rewriter) const {
341 // Bail if it's not a memref.
342 if (!isa<MemRefType>(op.getPtr().getType()))
343 return rewriter.notifyMatchFailure(op, "Expected a memref input");
344
345 // Extract the aligned pointer from the memref descriptor.
346 rewriter.replaceOp(
347 op, MemRefDescriptor(adaptor.getPtr()).alignedPtr(rewriter, op.getLoc()));
348 return success();
349}
350
351//===----------------------------------------------------------------------===//
352// TypeOffsetOpConversion
353//===----------------------------------------------------------------------===//
354
355LogicalResult TypeOffsetOpConversion::matchAndRewrite(
356 ptr::TypeOffsetOp op, OpAdaptor adaptor,
357 ConversionPatternRewriter &rewriter) const {
358 // Convert the type attribute.
359 Type type = getTypeConverter()->convertType(op.getElementType());
360 if (!type)
361 return rewriter.notifyMatchFailure(op, "Couldn't convert the type");
362
363 // Convert the result type.
364 Type rTy = getTypeConverter()->convertType(op.getResult().getType());
365 if (!rTy)
366 return rewriter.notifyMatchFailure(op, "Couldn't convert the result type");
367
368 // TODO: Use MLIR's data layout. We don't use it because overall support is
369 // still flaky.
370
371 // Create an LLVM pointer type for the GEP operation.
372 auto ptrTy = LLVM::LLVMPointerType::get(getContext());
373
374 // Create a GEP operation to compute the offset of the type.
375 auto offset =
376 LLVM::GEPOp::create(rewriter, op.getLoc(), ptrTy, type,
377 LLVM::ZeroOp::create(rewriter, op.getLoc(), ptrTy),
378 ArrayRef<LLVM::GEPArg>({LLVM::GEPArg(1)}));
379
380 // Replace the original op with a PtrToIntOp using the computed offset.
381 rewriter.replaceOpWithNewOp<LLVM::PtrToIntOp>(op, rTy, offset.getRes());
382 return success();
383}
384
385//===----------------------------------------------------------------------===//
386// ConstantOpConversion
387//===----------------------------------------------------------------------===//
388
389LogicalResult ConstantOpConversion::matchAndRewrite(
390 ptr::ConstantOp op, OpAdaptor adaptor,
391 ConversionPatternRewriter &rewriter) const {
392 TypedAttr value = op.getValue();
393 Type resultType = getTypeConverter()->convertType(op.getType());
394 if (!resultType)
395 return rewriter.notifyMatchFailure(op, "Couldn't convert the result type");
396
397 if (isa<ptr::NullAttr>(value)) {
398 rewriter.replaceOpWithNewOp<LLVM::ZeroOp>(op, resultType);
399 return success();
400 }
401 auto addrAttr = dyn_cast<ptr::AddressAttr>(value);
402 // Early-exit if unknown attribute.
403 if (!addrAttr) {
404 return rewriter.notifyMatchFailure(
405 op, "unsupported value attribute kind: " +
406 value.getAbstractAttribute().getName());
407 }
408 Type intType = rewriter.getIntegerType(addrAttr.getValue().getBitWidth());
409 Value intConst = LLVM::ConstantOp::create(rewriter, op.getLoc(), intType,
410 addrAttr.getValue());
411 rewriter.replaceOpWithNewOp<LLVM::IntToPtrOp>(op, resultType, intConst);
412 return success();
413}
414
415//===----------------------------------------------------------------------===//
416// ConvertToLLVMPatternInterface implementation
417//===----------------------------------------------------------------------===//
418
419namespace {
420/// Implement the interface to convert Ptr to LLVM.
421struct PtrToLLVMDialectInterface : public ConvertToLLVMPatternInterface {
422 PtrToLLVMDialectInterface(Dialect *dialect)
423 : ConvertToLLVMPatternInterface(dialect) {}
424
425 void loadDependentDialects(MLIRContext *context) const final {
426 context->loadDialect<LLVM::LLVMDialect>();
427 }
428
429 /// Hook for derived dialect interface to provide conversion patterns
430 /// and mark dialect legal for the conversion target.
431 void populateConvertToLLVMConversionPatterns(
432 ConversionTarget &target, LLVMTypeConverter &converter,
433 RewritePatternSet &patterns) const final {
434 ptr::populatePtrToLLVMConversionPatterns(converter, patterns);
435 }
436};
437} // namespace
438
439//===----------------------------------------------------------------------===//
440// API
441//===----------------------------------------------------------------------===//
442
444 LLVMTypeConverter &converter, RewritePatternSet &patterns) {
445 // Add address space conversions.
446 converter.addTypeAttributeConversion(
447 [&](PtrLikeTypeInterface type, ptr::GenericSpaceAttr memorySpace)
448 -> TypeConverter::AttributeConversionResult {
449 if (type.getMemorySpace() != memorySpace)
450 return TypeConverter::AttributeConversionResult::na();
451 return IntegerAttr::get(IntegerType::get(type.getContext(), 32), 0);
452 });
453
454 // Add type conversions.
455 converter.addConversion([&](ptr::PtrType type) -> Type {
456 std::optional<Attribute> maybeAttr =
457 converter.convertTypeAttribute(type, type.getMemorySpace());
458 auto memSpace =
459 maybeAttr ? dyn_cast_or_null<IntegerAttr>(*maybeAttr) : IntegerAttr();
460 if (!memSpace)
461 return {};
462 return LLVM::LLVMPointerType::get(type.getContext(),
463 memSpace.getValue().getSExtValue());
464 });
465
466 // Convert ptr metadata of memref type.
467 converter.addConversion([&](ptr::PtrMetadataType type) -> Type {
468 auto mTy = dyn_cast<MemRefType>(type.getType());
469 if (!mTy)
470 return {};
471 FailureOr<LLVM::LLVMStructType> res =
472 createMemRefMetadataType(mTy, converter);
473 return failed(res) ? Type() : res.value();
474 });
475
476 // Add conversion patterns.
477 patterns.add<FromPtrOpConversion, GetMetadataOpConversion, PtrAddOpConversion,
478 ToPtrOpConversion, TypeOffsetOpConversion, ConstantOpConversion>(
479 converter);
480}
481
483 registry.addExtension(+[](MLIRContext *ctx, ptr::PtrDialect *dialect) {
484 dialect->addInterfaces<PtrToLLVMDialectInterface>();
485 });
486}
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:227
ConvertOpToLLVMPattern(const LLVMTypeConverter &typeConverter, PatternBenefit benefit=1)
Definition Pattern.h:233
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:63
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:717
Include the generated interface declarations.