Skip to content

Commit 49edb9f

Browse files
committed
[mlir][memref][vectpr] Add alignment attribute to memory access ops
1 parent 01f9dff commit 49edb9f

File tree

9 files changed

+371
-6
lines changed

9 files changed

+371
-6
lines changed

mlir/include/mlir/Dialect/MemRef/IR/MemRefOps.td

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1217,6 +1217,11 @@ def LoadOp : MemRef_Op<"load",
12171217
be reused in the cache. For details, refer to the
12181218
[https://llvm.org/docs/LangRef.html#load-instruction](LLVM load instruction).
12191219

1220+
An optional `alignment` attribute allows to specify the byte alignment of the
1221+
load operation. It must be a positive power of 2. The operation must access
1222+
memory at an address aligned to this boundary. Violations may lead to
1223+
architecture-specific faults or performance penalties.
1224+
A value of 0 indicates no specific alignment requirement.
12201225
Example:
12211226

12221227
```mlir
@@ -1227,7 +1232,38 @@ def LoadOp : MemRef_Op<"load",
12271232
let arguments = (ins Arg<AnyMemRef, "the reference to load from",
12281233
[MemRead]>:$memref,
12291234
Variadic<Index>:$indices,
1230-
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal);
1235+
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal,
1236+
ConfinedAttr<OptionalAttr<I64Attr>,
1237+
[IntMinValue<0>]>:$alignment);
1238+
1239+
let builders = [
1240+
OpBuilder<(ins "Value":$memref,
1241+
"ValueRange":$indices,
1242+
CArg<"uint64_t", "0">:$alignment), [{
1243+
return build($_builder, $_state, memref, indices, false,
1244+
alignment != 0 ? $_builder.getI64IntegerAttr(alignment) :
1245+
nullptr);
1246+
}]>,
1247+
OpBuilder<(ins "Type":$resultType,
1248+
"Value":$memref,
1249+
"ValueRange":$indices,
1250+
CArg<"bool", "false">:$nontemporal,
1251+
CArg<"uint64_t", "0">:$alignment), [{
1252+
return build($_builder, $_state, resultType, memref, indices, nontemporal,
1253+
alignment != 0 ? $_builder.getI64IntegerAttr(alignment) :
1254+
nullptr);
1255+
}]>,
1256+
OpBuilder<(ins "TypeRange":$resultTypes,
1257+
"Value":$memref,
1258+
"ValueRange":$indices,
1259+
CArg<"bool", "false">:$nontemporal,
1260+
CArg<"uint64_t", "0">:$alignment), [{
1261+
return build($_builder, $_state, resultTypes, memref, indices, nontemporal,
1262+
alignment != 0 ? $_builder.getI64IntegerAttr(alignment) :
1263+
nullptr);
1264+
}]>
1265+
];
1266+
12311267
let results = (outs AnyType:$result);
12321268

12331269
let extraClassDeclaration = [{
@@ -1913,6 +1949,11 @@ def MemRef_StoreOp : MemRef_Op<"store",
19131949
be reused in the cache. For details, refer to the
19141950
[https://llvm.org/docs/LangRef.html#store-instruction](LLVM store instruction).
19151951

1952+
An optional `alignment` attribute allows to specify the byte alignment of the
1953+
store operation. It must be a positive power of 2. The operation must access
1954+
memory at an address aligned to this boundary. Violations may lead to
1955+
architecture-specific faults or performance penalties.
1956+
A value of 0 indicates no specific alignment requirement.
19161957
Example:
19171958

19181959
```mlir
@@ -1924,13 +1965,32 @@ def MemRef_StoreOp : MemRef_Op<"store",
19241965
Arg<AnyMemRef, "the reference to store to",
19251966
[MemWrite]>:$memref,
19261967
Variadic<Index>:$indices,
1927-
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal);
1968+
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal,
1969+
ConfinedAttr<OptionalAttr<I64Attr>,
1970+
[IntMinValue<0>]>:$alignment);
19281971

19291972
let builders = [
1973+
OpBuilder<(ins "Value":$valueToStore,
1974+
"Value":$memref,
1975+
"ValueRange":$indices,
1976+
CArg<"bool", "false">:$nontemporal,
1977+
CArg<"uint64_t", "0">:$alignment), [{
1978+
return build($_builder, $_state, valueToStore, memref, indices, nontemporal,
1979+
alignment != 0 ? $_builder.getI64IntegerAttr(alignment) :
1980+
nullptr);
1981+
}]>,
1982+
OpBuilder<(ins "Value":$valueToStore,
1983+
"Value":$memref,
1984+
"ValueRange":$indices,
1985+
"uint64_t":$alignment), [{
1986+
return build($_builder, $_state, valueToStore, memref, indices, false,
1987+
$_builder.getI64IntegerAttr(alignment));
1988+
}]>,
19301989
OpBuilder<(ins "Value":$valueToStore, "Value":$memref), [{
19311990
$_state.addOperands(valueToStore);
19321991
$_state.addOperands(memref);
1933-
}]>];
1992+
}]>
1993+
];
19341994

19351995
let extraClassDeclaration = [{
19361996
Value getValueToStore() { return getOperand(0); }

mlir/include/mlir/Dialect/Vector/IR/VectorOps.td

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,12 +1734,49 @@ def Vector_LoadOp : Vector_Op<"load"> {
17341734
```mlir
17351735
%result = vector.load %memref[%c0] : memref<7xf32>, vector<8xf32>
17361736
```
1737+
1738+
An optional `alignment` attribute allows to specify the byte alignment of the
1739+
load operation. It must be a positive power of 2. The operation must access
1740+
memory at an address aligned to this boundary. Violations may lead to
1741+
architecture-specific faults or performance penalties.
1742+
A value of 0 indicates no specific alignment requirement.
17371743
}];
17381744

17391745
let arguments = (ins Arg<AnyMemRef, "the reference to load from",
17401746
[MemRead]>:$base,
17411747
Variadic<Index>:$indices,
1742-
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal);
1748+
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal,
1749+
ConfinedAttr<OptionalAttr<I64Attr>,
1750+
[IntMinValue<0>]>:$alignment);
1751+
1752+
let builders = [
1753+
OpBuilder<(ins "VectorType":$resultType,
1754+
"Value":$base,
1755+
"ValueRange":$indices,
1756+
CArg<"bool", "false">:$nontemporal,
1757+
CArg<"uint64_t", "0">:$alignment), [{
1758+
return build($_builder, $_state, resultType, base, indices, nontemporal,
1759+
alignment != 0 ? $_builder.getI64IntegerAttr(alignment) :
1760+
nullptr);
1761+
}]>,
1762+
OpBuilder<(ins "VectorType":$resultType,
1763+
"Value":$base,
1764+
"ValueRange":$indices,
1765+
"uint64_t":$alignment), [{
1766+
return build($_builder, $_state, resultType, base, indices, false,
1767+
$_builder.getI64IntegerAttr(alignment));
1768+
}]>,
1769+
OpBuilder<(ins "TypeRange":$resultTypes,
1770+
"Value":$base,
1771+
"ValueRange":$indices,
1772+
CArg<"bool", "false">:$nontemporal,
1773+
CArg<"uint64_t", "0">:$alignment), [{
1774+
return build($_builder, $_state, resultTypes, base, indices, nontemporal,
1775+
alignment != 0 ? $_builder.getI64IntegerAttr(alignment) :
1776+
nullptr);
1777+
}]>
1778+
];
1779+
17431780
let results = (outs AnyVectorOfAnyRank:$result);
17441781

17451782
let extraClassDeclaration = [{
@@ -1818,15 +1855,41 @@ def Vector_StoreOp : Vector_Op<"store"> {
18181855
```mlir
18191856
vector.store %valueToStore, %memref[%c0] : memref<7xf32>, vector<8xf32>
18201857
```
1858+
1859+
An optional `alignment` attribute allows to specify the byte alignment of the
1860+
store operation. It must be a positive power of 2. The operation must access
1861+
memory at an address aligned to this boundary. Violations may lead to
1862+
architecture-specific faults or performance penalties.
1863+
A value of 0 indicates no specific alignment requirement.
18211864
}];
18221865

18231866
let arguments = (ins
18241867
AnyVectorOfAnyRank:$valueToStore,
18251868
Arg<AnyMemRef, "the reference to store to",
18261869
[MemWrite]>:$base,
18271870
Variadic<Index>:$indices,
1828-
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal
1829-
);
1871+
DefaultValuedOptionalAttr<BoolAttr, "false">:$nontemporal,
1872+
ConfinedAttr<OptionalAttr<I64Attr>,
1873+
[IntMinValue<0>]>:$alignment);
1874+
1875+
let builders = [
1876+
OpBuilder<(ins "Value":$valueToStore,
1877+
"Value":$base,
1878+
"ValueRange":$indices,
1879+
CArg<"bool", "false">:$nontemporal,
1880+
CArg<"uint64_t", "0">:$alignment), [{
1881+
return build($_builder, $_state, valueToStore, base, indices, nontemporal,
1882+
alignment != 0 ? $_builder.getI64IntegerAttr(alignment) :
1883+
nullptr);
1884+
}]>,
1885+
OpBuilder<(ins "Value":$valueToStore,
1886+
"Value":$base,
1887+
"ValueRange":$indices,
1888+
"uint64_t":$alignment), [{
1889+
return build($_builder, $_state, valueToStore, base, indices, false,
1890+
$_builder.getI64IntegerAttr(alignment));
1891+
}]>
1892+
];
18301893

18311894
let extraClassDeclaration = [{
18321895
MemRefType getMemRefType() {
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s
2+
3+
// CHECK-LABEL: func @test_load_store_alignment
4+
// CHECK: memref.load {{.*}} {alignment = 16 : i64}
5+
// CHECK: memref.store {{.*}} {alignment = 16 : i64}
6+
func.func @test_load_store_alignment(%memref: memref<4xi32>) {
7+
%c0 = arith.constant 0 : index
8+
%val = memref.load %memref[%c0] { alignment = 16 } : memref<4xi32>
9+
memref.store %val, %memref[%c0] { alignment = 16 } : memref<4xi32>
10+
return
11+
}
12+
13+
// -----
14+
15+
func.func @test_invalid_load_alignment(%memref: memref<4xi32>) {
16+
// expected-error @+1 {{custom op 'memref.load' 'memref.load' op attribute 'alignment' failed to satisfy constraint: 64-bit signless integer attribute whose minimum value is 0}}
17+
%val = memref.load %memref[%c0] { alignment = -1 } : memref<4xi32>
18+
return
19+
}
20+
21+
// -----
22+
23+
func.func @test_invalid_store_alignment(%memref: memref<4xi32>, %val: memref<4xi32>) {
24+
// expected-error @+1 {{custom op 'memref.store' 'memref.store' op attribute 'alignment' failed to satisfy constraint: 64-bit signless integer attribute whose minimum value is 0}}
25+
memref.store %val, %memref[%c0] { alignment = -1 } : memref<4xi32>
26+
return
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RUN: mlir-opt -split-input-file -verify-diagnostics %s | FileCheck %s
2+
3+
// CHECK-LABEL: func @test_load_store_alignment
4+
// CHECK: vector.load {{.*}} {alignment = 16 : i64}
5+
// CHECK: vector.store {{.*}} {alignment = 16 : i64}
6+
func.func @test_load_store_alignment(%memref: memref<4xi32>) {
7+
%c0 = arith.constant 0 : index
8+
%val = vector.load %memref[%c0] { alignment = 16 } : memref<4xi32>, vector<4xi32>
9+
vector.store %val, %memref[%c0] { alignment = 16 } : memref<4xi32>, vector<4xi32>
10+
return
11+
}
12+
13+
// -----
14+
15+
func.func @test_invalid_load_alignment(%memref: memref<4xi32>) {
16+
// expected-error @+1 {{custom op 'vector.load' 'vector.load' op attribute 'alignment' failed to satisfy constraint: 64-bit signless integer attribute whose minimum value is 0}}
17+
%val = vector.load %memref[%c0] { alignment = -1 } : memref<4xi32>, vector<4xi32>
18+
return
19+
}
20+
21+
// -----
22+
23+
func.func @test_invalid_store_alignment(%memref: memref<4xi32>, %val: vector<4xi32>) {
24+
// expected-error @+1 {{custom op 'vector.store' 'vector.store' op attribute 'alignment' failed to satisfy constraint: 64-bit signless integer attribute whose minimum value is 0}}
25+
vector.store %val, %memref[%c0] { alignment = -1 } : memref<4xi32>, vector<4xi32>
26+
return
27+
}

mlir/unittests/Dialect/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ add_subdirectory(SPIRV)
1818
add_subdirectory(SMT)
1919
add_subdirectory(Transform)
2020
add_subdirectory(Utils)
21+
add_subdirectory(Vector)

mlir/unittests/Dialect/MemRef/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
add_mlir_unittest(MLIRMemRefTests
22
InferShapeTest.cpp
3+
LoadStoreAlignment.cpp
34
)
45
mlir_target_link_libraries(MLIRMemRefTests
56
PRIVATE
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===- LoadStoreAlignment.cpp - unit tests for load/store alignment -------===//
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+
#include "mlir/Dialect/MemRef/IR/MemRef.h"
10+
#include "mlir/IR/Builders.h"
11+
#include "mlir/IR/Verifier.h"
12+
#include "gtest/gtest.h"
13+
14+
using namespace mlir;
15+
using namespace mlir::memref;
16+
17+
TEST(LoadStoreAlignmentTest, ValidAlignment) {
18+
MLIRContext ctx;
19+
OpBuilder b(&ctx);
20+
ctx.loadDialect<memref::MemRefDialect>();
21+
22+
// Create a dummy memref
23+
Type elementType = b.getI32Type();
24+
auto memrefType = MemRefType::get({4}, elementType);
25+
Value memref = b.create<memref::AllocaOp>(b.getUnknownLoc(), memrefType);
26+
27+
// Create load with valid alignment
28+
Value zero = b.create<arith::ConstantIndexOp>(b.getUnknownLoc(), 0);
29+
auto loadOp = b.create<LoadOp>(b.getUnknownLoc(), memref, ValueRange{zero},
30+
static_cast<uint64_t>(16));
31+
32+
// Verify the attribute exists
33+
auto alignmentAttr = loadOp->getAttrOfType<IntegerAttr>("alignment");
34+
EXPECT_TRUE(alignmentAttr != nullptr);
35+
EXPECT_EQ(alignmentAttr.getInt(), 16);
36+
37+
// Create store with valid alignment
38+
auto storeOp = b.create<StoreOp>(b.getUnknownLoc(), loadOp, memref,
39+
ValueRange{zero}, static_cast<uint64_t>(16));
40+
41+
// Verify the attribute exists
42+
alignmentAttr = storeOp->getAttrOfType<IntegerAttr>("alignment");
43+
EXPECT_TRUE(alignmentAttr != nullptr);
44+
EXPECT_EQ(alignmentAttr.getInt(), 16);
45+
}
46+
47+
TEST(LoadStoreAlignmentTest, InvalidAlignmentFailsVerification) {
48+
MLIRContext ctx;
49+
OpBuilder b(&ctx);
50+
ctx.loadDialect<memref::MemRefDialect>();
51+
52+
Type elementType = b.getI32Type();
53+
auto memrefType = MemRefType::get({4}, elementType);
54+
Value memref = b.create<memref::AllocaOp>(b.getUnknownLoc(), memrefType);
55+
56+
Value zero = b.create<arith::ConstantIndexOp>(b.getUnknownLoc(), 0);
57+
58+
auto loadOp =
59+
b.create<LoadOp>(b.getUnknownLoc(), memref, ValueRange{zero}, -1);
60+
61+
// Capture diagnostics
62+
std::string errorMessage;
63+
ScopedDiagnosticHandler handler(
64+
&ctx, [&](Diagnostic &diag) { errorMessage = diag.str(); });
65+
66+
// Trigger verification
67+
auto result = mlir::verify(loadOp);
68+
69+
// Check results
70+
EXPECT_TRUE(failed(result));
71+
EXPECT_EQ(
72+
errorMessage,
73+
"'memref.load' op attribute 'alignment' failed to satisfy constraint: "
74+
"64-bit signless integer attribute whose minimum value is 0");
75+
76+
auto storeOp = b.create<StoreOp>(b.getUnknownLoc(), loadOp, memref,
77+
ValueRange{zero}, false, -1);
78+
result = mlir::verify(storeOp);
79+
80+
// Check results
81+
EXPECT_TRUE(failed(result));
82+
EXPECT_EQ(
83+
errorMessage,
84+
"'memref.store' op attribute 'alignment' failed to satisfy constraint: "
85+
"64-bit signless integer attribute whose minimum value is 0");
86+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
add_mlir_unittest(MLIRVectorTests
2+
LoadStoreAlignment.cpp
3+
)
4+
mlir_target_link_libraries(MLIRVectorTests
5+
PRIVATE
6+
MLIRVectorDialect
7+
)

0 commit comments

Comments
 (0)