[mlir][EmitC] Add rank-0 MemRef conversion (#205774)
Add rank-0 memref support to MemRefToEmitC and the EmitC TypeConverter, needed for lowering the models generated by: - `llvm/lib/Analysis/models/gen-inline-oz-test-model.py` - `llvm/lib/Analysis/models/gen-regalloc-eviction-test-model.py` Rank-0 memrefs are no longer rejected by `isMemRefTypeLegalForEmitC`. The EmitC type converter maps `memref<T>` to `!emitc.ptr<T>`, giving rank-0 memrefs addressable scalar storage. `memref.alloc` now allocates one element for rank-0 memrefs. `memref.dealloc` frees the pointer-backed value. `memref.load` and `memref.store` lower rank-0 accesses through `emitc.subscript %ptr[0]`, followed by `emitc.load` or `emitc.assign`. `memref.copy` lowers rank-0 copies as scalar load plus assign. `memref.alloca` still rejects rank-0 memrefs because this patch only supports pointer-backed rank-0 lowering. The pointer recovery helper now accepts both direct `emitc.ptr<T>` values and pointer values behind `builtin.unrealized_conversion_cast`, which is needed by dealloc, copy, load, and store depending on where the value comes from in conversion. Tests cover rank-0 alloc/dealloc, load/store, alloc-backed load/store, and copy. Removed cases this patch adds support for (rank-0) from `func-to-emitc-failed`. Assisted-by: Codex (refine implementation + tests). I reviewed all code and tests before submission.
This commit is contained in:
1 parent
4b32060e21
commit
5ae6537bba
8 files changed
+183
-59
No files matched your search
@@ -18,7 +18,6 @@ namespace {
|
||||
|
||||
static bool isMemRefTypeLegalForEmitC(MemRefType memRefType) {
|
||||
return memRefType.hasStaticShape() && memRefType.getLayout().isIdentity() &&
|
||||
memRefType.getRank() != 0 &&
|
||||
!llvm::is_contained(memRefType.getShape(), 0);
|
||||
}
|
||||
|
||||
@@ -50,6 +49,8 @@ EmitCTypeConverter::EmitCTypeConverter(MLIRContext *ctx) {
|
||||
if (!convertedElementType)
|
||||
return {};
|
||||
|
||||
if (memRefType.getRank() == 0)
|
||||
return emitc::PointerType::get(convertedElementType);
|
||||
return emitc::ArrayType::get(memRefType.getShape(), convertedElementType);
|
||||
});
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ using namespace mlir;
|
||||
|
||||
static bool isMemRefTypeLegalForEmitC(MemRefType memRefType) {
|
||||
return memRefType.hasStaticShape() && memRefType.getLayout().isIdentity() &&
|
||||
memRefType.getRank() != 0 &&
|
||||
!llvm::is_contained(memRefType.getShape(), 0);
|
||||
}
|
||||
|
||||
@@ -80,26 +79,31 @@ struct ConvertAlloca final : public OpConversionPattern<memref::AllocaOp> {
|
||||
op.getLoc(), "cannot transform alloca with alignment requirement");
|
||||
}
|
||||
|
||||
auto resultTy = getTypeConverter()->convertType(op.getType());
|
||||
if (!resultTy) {
|
||||
Type resultTy = getTypeConverter()->convertType(op.getType());
|
||||
if (!resultTy)
|
||||
return rewriter.notifyMatchFailure(op.getLoc(), "cannot convert type");
|
||||
}
|
||||
|
||||
auto noInit = emitc::OpaqueAttr::get(getContext(), "");
|
||||
// Rank-0 path
|
||||
if (op.getType().getRank() == 0) {
|
||||
auto pointerTy = dyn_cast<emitc::PointerType>(resultTy);
|
||||
assert(pointerTy && "expected rank-0 MemRef to convert to pointer");
|
||||
Type elemTy = pointerTy.getPointee();
|
||||
auto var = emitc::VariableOp::create(
|
||||
rewriter, op.getLoc(), emitc::LValueType::get(elemTy), noInit);
|
||||
|
||||
auto ptr = emitc::AddressOfOp::create(rewriter, op.getLoc(), resultTy,
|
||||
var.getResult());
|
||||
|
||||
rewriter.replaceOp(op, ptr.getResult());
|
||||
return success();
|
||||
}
|
||||
// Rank > 0 path
|
||||
rewriter.replaceOpWithNewOp<emitc::VariableOp>(op, resultTy, noInit);
|
||||
return success();
|
||||
}
|
||||
};
|
||||
|
||||
Type convertMemRefType(MemRefType opTy, const TypeConverter *typeConverter) {
|
||||
Type resultTy;
|
||||
if (opTy.getRank() == 0) {
|
||||
resultTy = typeConverter->convertType(mlir::getElementTypeOrSelf(opTy));
|
||||
} else {
|
||||
resultTy = typeConverter->convertType(opTy);
|
||||
}
|
||||
return resultTy;
|
||||
}
|
||||
|
||||
static Value calculateMemrefTotalSizeBytes(Location loc, MemRefType memrefType,
|
||||
OpBuilder &builder,
|
||||
Type convertedElementType) {
|
||||
@@ -142,9 +146,12 @@ createPointerFromEmitcArray(Location loc, OpBuilder &builder,
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// If `v` is defined through an unrealized cast and the source of that cast
|
||||
// is `emitc.ptr`, return the pointer.
|
||||
static Value stripPointerUnrealizedCast(Value v) {
|
||||
static Value getMemRefPointer(Value v) {
|
||||
if (isa<emitc::PointerType>(v.getType()))
|
||||
return v;
|
||||
|
||||
// If `v` is defined through an unrealized cast and the source of that cast
|
||||
// is `emitc.ptr`, return the pointer.
|
||||
if (auto cast = v.getDefiningOp<UnrealizedConversionCastOp>())
|
||||
if (cast.getNumOperands() == 1 &&
|
||||
isa<emitc::PointerType>(cast.getOperand(0).getType()))
|
||||
@@ -165,6 +172,9 @@ static Value computeRowMajorLinearIndex(ImplicitLocOpBuilder &builder,
|
||||
? emitc::ConstantOp::create(builder, idxType, builder.getIndexAttr(0))
|
||||
: indices[0];
|
||||
|
||||
if (indices.empty())
|
||||
return linearIndex;
|
||||
|
||||
for (auto [dim, idx] : llvm::zip(shape.drop_front(), indices.drop_front())) {
|
||||
Value dimSize =
|
||||
emitc::ConstantOp::create(builder, idxType, builder.getIndexAttr(dim));
|
||||
@@ -238,10 +248,9 @@ struct ConvertDealloc final : public OpConversionPattern<memref::DeallocOp> {
|
||||
ConversionPatternRewriter &rewriter) const override {
|
||||
Location loc = deallocOp.getLoc();
|
||||
// `free` can only be emitted when the dealloc operand is recoverable as an
|
||||
// `emitc.ptr<T>`. In the current conversion, that happens via an
|
||||
// unrealized_conversion_cast from the pointer-backed EmitC form.
|
||||
Value strippedPtr = stripPointerUnrealizedCast(operands.getMemref());
|
||||
if (!strippedPtr) {
|
||||
// `emitc.ptr<T>`.
|
||||
Value ptr = getMemRefPointer(operands.getMemref());
|
||||
if (!ptr) {
|
||||
return rewriter.notifyMatchFailure(
|
||||
loc, "expected pointer-backed memref for EmitC deallocation");
|
||||
}
|
||||
@@ -252,7 +261,7 @@ struct ConvertDealloc final : public OpConversionPattern<memref::DeallocOp> {
|
||||
Type opaqueVoidPtrType = emitc::PointerType::get(
|
||||
emitc::OpaqueType::get(rewriter.getContext(), "void"));
|
||||
Value freeArg =
|
||||
emitc::CastOp::create(rewriter, loc, opaqueVoidPtrType, strippedPtr);
|
||||
emitc::CastOp::create(rewriter, loc, opaqueVoidPtrType, ptr);
|
||||
emitc::CallOpaqueOp freeCall = emitc::CallOpaqueOp::create(
|
||||
rewriter, loc, TypeRange{}, rewriter.getStringAttr(freeFunctionName),
|
||||
ValueRange{freeArg});
|
||||
@@ -280,6 +289,34 @@ struct ConvertCopy final : public OpConversionPattern<memref::CopyOp> {
|
||||
return rewriter.notifyMatchFailure(
|
||||
loc, "incompatible target memref type for EmitC conversion");
|
||||
|
||||
if (srcMemrefType.getRank() == 0) {
|
||||
assert(targetMemrefType.getRank() == 0 &&
|
||||
"target must have same rank as source");
|
||||
Type elementType =
|
||||
getTypeConverter()->convertType(srcMemrefType.getElementType());
|
||||
if (!elementType)
|
||||
return rewriter.notifyMatchFailure(loc, "cannot convert element type");
|
||||
|
||||
Value srcPtr = getMemRefPointer(operands.getSource());
|
||||
Value targetPtr = getMemRefPointer(operands.getTarget());
|
||||
if (!srcPtr || !targetPtr)
|
||||
return rewriter.notifyMatchFailure(loc, "expected pointer operands");
|
||||
|
||||
Value zeroIndex = emitc::ConstantOp::create(
|
||||
rewriter, loc, rewriter.getIndexType(), rewriter.getIndexAttr(0));
|
||||
Value srcLValue = emitc::SubscriptOp::create(
|
||||
rewriter, loc, cast<TypedValue<emitc::PointerType>>(srcPtr),
|
||||
zeroIndex);
|
||||
Value value =
|
||||
emitc::LoadOp::create(rewriter, loc, elementType, srcLValue);
|
||||
|
||||
Value targetLValue = emitc::SubscriptOp::create(
|
||||
rewriter, loc, cast<TypedValue<emitc::PointerType>>(targetPtr),
|
||||
zeroIndex);
|
||||
rewriter.replaceOpWithNewOp<emitc::AssignOp>(copyOp, targetLValue, value);
|
||||
return success();
|
||||
}
|
||||
|
||||
auto srcArrayValue =
|
||||
cast<TypedValue<emitc::ArrayType>>(operands.getSource());
|
||||
emitc::AddressOfOp srcPtr =
|
||||
@@ -331,7 +368,7 @@ struct ConvertGlobal final : public OpConversionPattern<memref::GlobalOp> {
|
||||
"currently not supported");
|
||||
}
|
||||
|
||||
Type resultTy = convertMemRefType(opTy, getTypeConverter());
|
||||
Type resultTy = getTypeConverter()->convertType(opTy);
|
||||
|
||||
if (!resultTy) {
|
||||
return rewriter.notifyMatchFailure(op.getLoc(),
|
||||
@@ -352,6 +389,9 @@ struct ConvertGlobal final : public OpConversionPattern<memref::GlobalOp> {
|
||||
|
||||
Attribute initialValue = operands.getInitialValueAttr();
|
||||
if (opTy.getRank() == 0) {
|
||||
auto pointerTy = dyn_cast<emitc::PointerType>(resultTy);
|
||||
assert(pointerTy && "expected rank-0 MemRef to convert to pointer");
|
||||
resultTy = pointerTy.getPointee();
|
||||
// special case for `variable : memref<i32> = dense<-1>`
|
||||
if (std::optional<Attribute> initValueAttr = op.getInitialValue()) {
|
||||
if (auto elementsAttr = llvm::dyn_cast<ElementsAttr>(*initValueAttr)) {
|
||||
@@ -378,7 +418,7 @@ struct ConvertGetGlobal final
|
||||
ConversionPatternRewriter &rewriter) const override {
|
||||
|
||||
MemRefType opTy = op.getType();
|
||||
Type resultTy = convertMemRefType(opTy, getTypeConverter());
|
||||
Type resultTy = getTypeConverter()->convertType(opTy);
|
||||
|
||||
if (!resultTy) {
|
||||
return rewriter.notifyMatchFailure(op.getLoc(),
|
||||
@@ -386,11 +426,13 @@ struct ConvertGetGlobal final
|
||||
}
|
||||
|
||||
if (opTy.getRank() == 0) {
|
||||
emitc::LValueType lvalueType = emitc::LValueType::get(resultTy);
|
||||
auto pointerTy = dyn_cast<emitc::PointerType>(resultTy);
|
||||
assert(pointerTy && "expected rank-0 MemRef to convert to pointer");
|
||||
Type elemTy = pointerTy.getPointee();
|
||||
emitc::LValueType lvalueType = emitc::LValueType::get(elemTy);
|
||||
emitc::GetGlobalOp globalLValue = emitc::GetGlobalOp::create(
|
||||
rewriter, op.getLoc(), lvalueType, operands.getNameAttr());
|
||||
emitc::PointerType pointerType = emitc::PointerType::get(resultTy);
|
||||
rewriter.replaceOpWithNewOp<emitc::AddressOfOp>(op, pointerType,
|
||||
rewriter.replaceOpWithNewOp<emitc::AddressOfOp>(op, resultTy,
|
||||
globalLValue);
|
||||
return success();
|
||||
}
|
||||
@@ -414,8 +456,8 @@ struct ConvertLoad final : public OpConversionPattern<memref::LoadOp> {
|
||||
|
||||
auto arrayValue =
|
||||
dyn_cast<TypedValue<emitc::ArrayType>>(operands.getMemref());
|
||||
Value strippedPtr = stripPointerUnrealizedCast(operands.getMemref());
|
||||
if (!strippedPtr && arrayValue) {
|
||||
Value ptr = getMemRefPointer(operands.getMemref());
|
||||
if (!ptr && arrayValue) {
|
||||
auto subscript = emitc::SubscriptOp::create(rewriter, loc, arrayValue,
|
||||
operands.getIndices());
|
||||
|
||||
@@ -423,14 +465,14 @@ struct ConvertLoad final : public OpConversionPattern<memref::LoadOp> {
|
||||
return success();
|
||||
}
|
||||
|
||||
if (!strippedPtr)
|
||||
if (!ptr)
|
||||
return rewriter.notifyMatchFailure(loc, "expected array or pointer type");
|
||||
MemRefType opMemrefType = cast<MemRefType>(op.getMemref().getType());
|
||||
ValueRange indices = operands.getIndices();
|
||||
|
||||
ImplicitLocOpBuilder b(loc, rewriter);
|
||||
Value linearIndex = computeRowMajorLinearIndex(b, opMemrefType, indices);
|
||||
auto typedPtr = cast<TypedValue<emitc::PointerType>>(strippedPtr);
|
||||
auto typedPtr = cast<TypedValue<emitc::PointerType>>(ptr);
|
||||
auto subscript =
|
||||
emitc::SubscriptOp::create(rewriter, loc, typedPtr, linearIndex);
|
||||
|
||||
@@ -448,8 +490,8 @@ struct ConvertStore final : public OpConversionPattern<memref::StoreOp> {
|
||||
Location loc = op.getLoc();
|
||||
auto arrayValue =
|
||||
dyn_cast<TypedValue<emitc::ArrayType>>(operands.getMemref());
|
||||
Value strippedPtr = stripPointerUnrealizedCast(operands.getMemref());
|
||||
if (!strippedPtr && arrayValue) {
|
||||
Value ptr = getMemRefPointer(operands.getMemref());
|
||||
if (!ptr && arrayValue) {
|
||||
auto subscript = emitc::SubscriptOp::create(rewriter, loc, arrayValue,
|
||||
operands.getIndices());
|
||||
rewriter.replaceOpWithNewOp<emitc::AssignOp>(op, subscript,
|
||||
@@ -457,14 +499,14 @@ struct ConvertStore final : public OpConversionPattern<memref::StoreOp> {
|
||||
return success();
|
||||
}
|
||||
|
||||
if (!strippedPtr)
|
||||
if (!ptr)
|
||||
return rewriter.notifyMatchFailure(loc, "expected array or pointer type");
|
||||
MemRefType opMemrefType = cast<MemRefType>(op.getMemref().getType());
|
||||
ValueRange indices = operands.getIndices();
|
||||
|
||||
ImplicitLocOpBuilder b(loc, rewriter);
|
||||
Value linearIndex = computeRowMajorLinearIndex(b, opMemrefType, indices);
|
||||
auto typedPtr = cast<TypedValue<emitc::PointerType>>(strippedPtr);
|
||||
auto typedPtr = cast<TypedValue<emitc::PointerType>>(ptr);
|
||||
auto subscript =
|
||||
emitc::SubscriptOp::create(rewriter, loc, typedPtr, linearIndex);
|
||||
|
||||
|
||||
@@ -7,21 +7,6 @@ func.func @unsuppoted_emitc_type(%arg0: i4) -> i4 {
|
||||
|
||||
// -----
|
||||
|
||||
// expected-error@+1 {{failed to legalize operation 'func.func'}}
|
||||
func.func private @return_rank0_alloc() -> memref<i32> {
|
||||
%alloc = memref.alloc() : memref<i32>
|
||||
return %alloc : memref<i32>
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// expected-error@+1 {{failed to legalize operation 'func.func'}}
|
||||
func.func private @return_rank0_arg(%arg0: memref<i32>) -> memref<i32> {
|
||||
return %arg0 : memref<i32>
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// expected-error@+1 {{failed to legalize operation 'func.func'}}
|
||||
func.func private @return_rank1_alloc() -> memref<1xi32> {
|
||||
%alloc = memref.alloc() : memref<1xi32>
|
||||
|
||||
@@ -89,3 +89,29 @@ func.func @allocating_and_deallocating_multi() {
|
||||
// NOCPP-NEXT: %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
|
||||
// NOCPP-NEXT: emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
|
||||
// NOCPP-NEXT: return
|
||||
|
||||
func.func @alloc_and_dealloc_rank0() {
|
||||
%alloc = memref.alloc() : memref<i32>
|
||||
memref.dealloc %alloc : memref<i32>
|
||||
return
|
||||
}
|
||||
|
||||
// CPP-LABEL: alloc_and_dealloc_rank0
|
||||
// CPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
|
||||
// CPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index
|
||||
// CPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
|
||||
// CPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
|
||||
// CPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
|
||||
// CPP-NEXT: %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
|
||||
// CPP-NEXT: emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
|
||||
// CPP-NEXT: return
|
||||
|
||||
// NOCPP-LABEL: alloc_and_dealloc_rank0
|
||||
// NOCPP-NEXT: %[[ALLOC:.*]] = emitc.call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
|
||||
// NOCPP-NEXT: %[[ALLOC_SIZE:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index
|
||||
// NOCPP-NEXT: %[[ALLOC_TOTAL_SIZE:.*]] = emitc.mul %[[ALLOC]], %[[ALLOC_SIZE]] : (!emitc.size_t, index) -> !emitc.size_t
|
||||
// NOCPP-NEXT: %[[ALLOC_PTR:.*]] = emitc.call_opaque "malloc"(%[[ALLOC_TOTAL_SIZE]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
|
||||
// NOCPP-NEXT: %[[ALLOC_CAST:.*]] = emitc.cast %[[ALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
|
||||
// NOCPP-NEXT: %[[FREE_PTR:.*]] = emitc.cast %[[ALLOC_CAST]] : !emitc.ptr<i32> to !emitc.ptr<!emitc.opaque<"void">>
|
||||
// NOCPP-NEXT: emitc.call_opaque "free"(%[[FREE_PTR]]) : (!emitc.ptr<!emitc.opaque<"void">>) -> ()
|
||||
// NOCPP-NEXT: return
|
||||
@@ -72,3 +72,51 @@ func.func @memref_load_store(%buff0: memref<2xf32>,
|
||||
memref.store %v, %buff1[%i, %j] : memref<4x8xf32>
|
||||
return
|
||||
}
|
||||
|
||||
/// Rank-0 alloc-backed load/store lower through pointer subscript at index 0.
|
||||
// CHECK-LABEL: emitc.func private @memref_alloc_store_rank0(
|
||||
// CHECK-SAME: %[[VAL:.*]]: i32)
|
||||
func.func private @memref_alloc_store_rank0(%v : i32) {
|
||||
// CHECK: %[[SIZEOF_I32:.*]] = call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
|
||||
// CHECK: %[[NUM_ELEMS:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index
|
||||
// CHECK: %[[TOTAL_BYTES:.*]] = mul %[[SIZEOF_I32]], %[[NUM_ELEMS]] : (!emitc.size_t, index) -> !emitc.size_t
|
||||
// CHECK: %[[MALLOC_PTR:.*]] = call_opaque "malloc"(%[[TOTAL_BYTES]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
|
||||
// CHECK: %[[ELEM_PTR:.*]] = cast %[[MALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
|
||||
%alloc = memref.alloc() : memref<i32>
|
||||
// CHECK: %[[ZERO:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index
|
||||
// CHECK: %[[ELEM_LVALUE:.*]] = subscript %[[ELEM_PTR]]{{\[}}%[[ZERO]]] : (!emitc.ptr<i32>, index) -> !emitc.lvalue<i32>
|
||||
// CHECK: assign %[[VAL]] : i32 to %[[ELEM_LVALUE]] : <i32>
|
||||
memref.store %v, %alloc[] : memref<i32>
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK-LABEL: emitc.func private @memref_alloc_load_rank0() -> i32
|
||||
func.func private @memref_alloc_load_rank0() -> i32 {
|
||||
// CHECK: %[[SIZEOF_I32:.*]] = call_opaque "sizeof"() <{args = [i32]}> : () -> !emitc.size_t
|
||||
// CHECK: %[[NUM_ELEMS:.*]] = "emitc.constant"() <{value = 1 : index}> : () -> index
|
||||
// CHECK: %[[TOTAL_BYTES:.*]] = mul %[[SIZEOF_I32]], %[[NUM_ELEMS]] : (!emitc.size_t, index) -> !emitc.size_t
|
||||
// CHECK: %[[MALLOC_PTR:.*]] = call_opaque "malloc"(%[[TOTAL_BYTES]]) : (!emitc.size_t) -> !emitc.ptr<!emitc.opaque<"void">>
|
||||
// CHECK: %[[ELEM_PTR:.*]] = cast %[[MALLOC_PTR]] : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<i32>
|
||||
%alloc = memref.alloc() : memref<i32>
|
||||
// CHECK: %[[ZERO:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index
|
||||
// CHECK: %[[ELEM_LVALUE:.*]] = subscript %[[ELEM_PTR]]{{\[}}%[[ZERO]]] : (!emitc.ptr<i32>, index) -> !emitc.lvalue<i32>
|
||||
// CHECK: %[[LOADED_VAL:.*]] = load %[[ELEM_LVALUE]] : <i32>
|
||||
%v = memref.load %alloc[] : memref<i32>
|
||||
// CHECK: return %[[LOADED_VAL]] : i32
|
||||
return %v : i32
|
||||
}
|
||||
|
||||
// CHECK-LABEL: emitc.func @memref_load_store_rank0(
|
||||
// CHECK-SAME: %[[SRC:.*]]: !emitc.ptr<i32>,
|
||||
// CHECK-SAME: %[[DST:.*]]: !emitc.ptr<i32>)
|
||||
func.func @memref_load_store_rank0(%src: memref<i32>, %dst: memref<i32>) {
|
||||
// CHECK: %[[LOAD_ZERO:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index
|
||||
// CHECK: %[[SRC_LVALUE:.*]] = subscript %[[SRC]]{{\[}}%[[LOAD_ZERO]]] : (!emitc.ptr<i32>, index) -> !emitc.lvalue<i32>
|
||||
// CHECK: %[[VAL:.*]] = load %[[SRC_LVALUE]] : <i32>
|
||||
%v = memref.load %src[] : memref<i32>
|
||||
// CHECK: %[[STORE_ZERO:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index
|
||||
// CHECK: %[[DST_LVALUE:.*]] = subscript %[[DST]]{{\[}}%[[STORE_ZERO]]] : (!emitc.ptr<i32>, index) -> !emitc.lvalue<i32>
|
||||
// CHECK: assign %[[VAL]] : i32 to %[[DST_LVALUE]] : <i32>
|
||||
memref.store %v, %dst[] : memref<i32>
|
||||
return
|
||||
}
|
||||
@@ -28,3 +28,24 @@ func.func @copying(%arg0 : memref<9x4x5x7xf32>, %arg1 : memref<9x4x5x7xf32>) {
|
||||
// CHECK: return
|
||||
// CHECK: }
|
||||
|
||||
// -----
|
||||
|
||||
func.func @copying_rank0(%arg0 : memref<i32>, %arg1 : memref<i32>) {
|
||||
memref.copy %arg0, %arg1 : memref<i32> to memref<i32>
|
||||
return
|
||||
}
|
||||
|
||||
// CHECK: module {
|
||||
|
||||
// CHECK-LABEL: func.func @copying_rank0(
|
||||
// CHECK-SAME: %[[ARG0:.*]]: memref<i32>,
|
||||
// CHECK-SAME: %[[ARG1:.*]]: memref<i32>) {
|
||||
// CHECK: %[[TARGET_CAST:.*]] = builtin.unrealized_conversion_cast %[[ARG1]] : memref<i32> to !emitc.ptr<i32>
|
||||
// CHECK: %[[SOURCE_CAST:.*]] = builtin.unrealized_conversion_cast %[[ARG0]] : memref<i32> to !emitc.ptr<i32>
|
||||
// CHECK: %[[ZERO:.*]] = "emitc.constant"() <{value = 0 : index}> : () -> index
|
||||
// CHECK: %[[SOURCE_LVALUE:.*]] = emitc.subscript %[[SOURCE_CAST]]{{\[}}%[[ZERO]]] : (!emitc.ptr<i32>, index) -> !emitc.lvalue<i32>
|
||||
// CHECK: %[[VALUE:.*]] = emitc.load %[[SOURCE_LVALUE]] : <i32>
|
||||
// CHECK: %[[TARGET_LVALUE:.*]] = emitc.subscript %[[TARGET_CAST]]{{\[}}%[[ZERO]]] : (!emitc.ptr<i32>, index) -> !emitc.lvalue<i32>
|
||||
// CHECK: emitc.assign %[[VALUE]] : i32 to %[[TARGET_LVALUE]] : <i32>
|
||||
// CHECK: return
|
||||
// CHECK: }
|
||||
@@ -72,14 +72,6 @@ func.func @non_identity_layout() {
|
||||
|
||||
// -----
|
||||
|
||||
func.func @zero_rank() {
|
||||
// expected-error@+1 {{failed to legalize operation 'memref.alloca'}}
|
||||
%0 = memref.alloca() : memref<f32>
|
||||
return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
func.func @zero_dim_rank_1() {
|
||||
// expected-error@+1 {{failed to legalize operation 'memref.alloca'}}
|
||||
%0 = memref.alloca() : memref<0xf32>
|
||||
|
||||
@@ -10,6 +10,15 @@ func.func @alloca() {
|
||||
|
||||
// -----
|
||||
|
||||
func.func @alloca_rank0() {
|
||||
// CHECK: %[[LVALUE:.*]] = "emitc.variable"() <{value = #emitc.opaque<"">}> : () -> !emitc.lvalue<f32>
|
||||
// CHECK: %[[PTR:.*]] = emitc.address_of %[[LVALUE]] : !emitc.lvalue<f32>
|
||||
%0 = memref.alloca() : memref<f32>
|
||||
return
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
// CHECK-LABEL: memref_store
|
||||
// CHECK-SAME: %[[buff:.*]]: memref<4x8xf32>, %[[v:.*]]: f32, %[[i:.*]]: index, %[[j:.*]]: index
|
||||
func.func @memref_store(%buff : memref<4x8xf32>, %v : f32, %i: index, %j: index) {
|
||||
|
||||
Reference in new issue
Block a user