Skip to content

Commit f53923d

Browse files
committed
[i1] Implement packed_storage layout encoding attribute
* make `packed_storage` as a type of `iree_encoding` attribute, and make type converters accept it. * `i1` tensors with `#iree_encoding.packed_storage` will be interpreted as packed i1 type, same as specifying `--iree-experimental-packed-i1-storage`. * `--iree-experimental-packed-i1-storage` are kept for testing purposes. We can drop this option after frontend enables emitting `i1` tensors with attributes. Signed-off-by: Alan Li <me@alanli.org>
1 parent 67a05a4 commit f53923d

File tree

9 files changed

+90
-18
lines changed

9 files changed

+90
-18
lines changed

compiler/src/iree/compiler/Codegen/Common/EncodingUtils.cpp

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "iree/compiler/Codegen/Common/EncodingUtils.h"
88
#include "iree/compiler/Codegen/Dialect/Codegen/Utils/Utils.h"
9+
#include "iree/compiler/Dialect/Encoding/IR/EncodingTypes.h"
910
#include "mlir/Dialect/Linalg/IR/LinalgInterfaces.h"
1011
#include "mlir/Dialect/Tensor/IR/Tensor.h"
1112
#include "mlir/Dialect/Utils/IndexingUtils.h"
@@ -64,7 +65,10 @@ MaterializeEncodingConversionTarget::MaterializeEncodingConversionTarget(
6465
markUnknownOpDynamicallyLegal([](Operation *op) {
6566
auto typeHasEncoding = [](Type t) -> bool {
6667
auto tensorType = dyn_cast<RankedTensorType>(t);
67-
return tensorType && tensorType.getEncoding();
68+
if (!(tensorType && tensorType.getEncoding()))
69+
return false;
70+
// Allow iree_encoding::packed_storage to pass through.
71+
return !IREE::Encoding::hasPackedStorageAttr(tensorType);
6872
};
6973
auto valueHasEncoding = [=](Value v) -> bool {
7074
return typeHasEncoding(v.getType());

compiler/src/iree/compiler/Dialect/Encoding/IR/EncodingAttrs.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ EncodingAttr getEncodingAttr(RankedTensorType type) {
243243
return dyn_cast_or_null<EncodingAttr>(type.getEncoding());
244244
}
245245

246+
bool hasPackedStorageAttr(RankedTensorType type) {
247+
return dyn_cast_or_null<PackedStorageAttr>(type.getEncoding()) != nullptr;
248+
}
249+
246250
FailureOr<linalg::ContractionDimensions>
247251
getEncodingContractionDims(EncodingAttr encoding) {
248252
auto indexingMapsAttr = encoding.getUserIndexingMaps();

compiler/src/iree/compiler/Dialect/Encoding/IR/EncodingAttrs.td

+11
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ def EncodingOpType : IREEEncoding_I32EnumAttr<"EncodingOpType",
4141
def EncodingOpTypeAttr:
4242
IREEEncoding_EnumAttr<EncodingOpType, "optype">;
4343

44+
45+
def PackedStorageAttr : IREEEncoding_Attr<"PackedStorage"> {
46+
let mnemonic = "packed_storage";
47+
let summary = [{Indicates packed storage datatype.}];
48+
let description = [{
49+
This attribute indicates this is a back-to-back packed storage in memory.
50+
This attribute takes no arguments.
51+
}];
52+
let genVerifyDecl = 0;
53+
}
54+
4455
def EncodingAttr :
4556
IREEEncoding_Attr<"Encoding", [
4657
DeclareAttrInterfaceMethods<IREEEncoding_EncodingLayoutAttrInterface, [

compiler/src/iree/compiler/Dialect/Encoding/IR/EncodingTypes.h

+3
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ namespace mlir::iree_compiler::IREE::Encoding {
3838
/// Otherwise, returns null.
3939
EncodingAttr getEncodingAttr(RankedTensorType type);
4040

41+
/// Returns true if the type contains packed_storage attribute.
42+
bool hasPackedStorageAttr(RankedTensorType type);
43+
4144
/// Returns the ContractionDimensions for the encoding user_indexing_maps.
4245
FailureOr<linalg::ContractionDimensions>
4346
getEncodingContractionDims(EncodingAttr encoding);

compiler/src/iree/compiler/Dialect/Stream/Conversion/HALToStream/Patterns.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
#include "iree/compiler/Dialect/Stream/IR/StreamOps.h"
1313
#include "mlir/Dialect/Arith/IR/Arith.h"
1414

15+
namespace mlir::iree_compiler::IREE::Encoding {
16+
bool hasPackedStorageAttr(mlir::RankedTensorType);
17+
} // namespace mlir::iree_compiler::IREE::Encoding
18+
1519
namespace mlir::iree_compiler {
1620

1721
namespace {
@@ -90,6 +94,11 @@ struct ConvertTensorImportOp
9094
RankedTensorType tensorType,
9195
ValueRange dynamicDims,
9296
OpBuilder &builder) {
97+
// If the encoding attr is about packed storage then we don't need all this
98+
if (IREE::Encoding::hasPackedStorageAttr(tensorType)) {
99+
return success();
100+
}
101+
93102
auto expectedElementType = builder.create<IREE::HAL::ElementTypeOp>(
94103
loc, tensorType.getElementType());
95104
auto expectedEncodingType = builder.create<IREE::HAL::EncodingTypeOp>(

compiler/src/iree/compiler/Dialect/Stream/Transforms/EncodeTensors.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ static LogicalResult checkEncoding(Operation *op, RankedTensorType encodingType,
4646
ValueRange encodingDims,
4747
PatternRewriter &rewriter) {
4848
auto encoding = encodingType.getEncoding();
49-
if (encoding && !llvm::isa<IREE::Encoding::EncodingAttr>(encoding)) {
49+
if (encoding && !llvm::isa<IREE::Encoding::EncodingAttr,
50+
IREE::Encoding::PackedStorageAttr>(encoding)) {
5051
return rewriter.notifyMatchFailure(op, [=](Diagnostic &d) {
5152
d << "unsupported tensor encoding: " << encodingType;
5253
});

compiler/src/iree/compiler/Dialect/Stream/Transforms/test/encode_host_tensors_packing_i1.mlir

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// RUN: iree-opt --split-input-file --iree-stream-encode-host-tensors --iree-experimental-packed-i1-storage %s | FileCheck %s
1+
// RUN: iree-opt --split-input-file --iree-stream-encode-host-tensors %s | FileCheck %s
22

3+
#packed = #iree_encoding.packed_storage
34
func.func @unaligned_i1_size() -> index {
4-
%0 = stream.tensor.sizeof tensor<12xi1> : index
5+
%0 = stream.tensor.sizeof tensor<12xi1, #packed> : index
56
return %0 : index
67
}
78
// CHECK: func @unaligned_i1_size() -> index {
@@ -10,8 +11,9 @@ func.func @unaligned_i1_size() -> index {
1011

1112
// -----
1213

14+
#packed = #iree_encoding.packed_storage
1315
func.func @aligned_i1_size() -> index {
14-
%0 = stream.tensor.sizeof tensor<24xi1> : index
16+
%0 = stream.tensor.sizeof tensor<24xi1, #packed> : index
1517
return %0 : index
1618
}
1719

compiler/src/iree/compiler/Utils/ElementPackingUtils.cpp

+40-13
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,28 @@
1515
#include "mlir/Dialect/Arith/IR/Arith.h"
1616
#include "mlir/IR/BuiltinTypes.h"
1717

18-
namespace mlir::iree_compiler {
19-
2018
llvm::cl::opt<bool> clEnableI1Support(
2119
"iree-experimental-packed-i1-storage",
2220
llvm::cl::desc(
23-
"Experimental feature: enable i1 data type support in codegen"),
21+
"Experimental feature: force to use packed storage for i1 tensors."
22+
"Turning on this option will see i1 tensors as if it has "
23+
"#iree_encoding.packed_storage attribute."
24+
"This is to allow an alternative way to test the packed storage "
25+
"feature before frontend can emit packed i1 tensors."
26+
"This option can be dropped once the frontend can emit packed i1 "
27+
"tensors."),
2428
llvm::cl::init(false));
2529

30+
namespace mlir::iree_compiler {
31+
2632
bool needToPackSubByteElementBitWidth(unsigned bitWidth) {
33+
return needToPackSubByteElementBitWidth(
34+
bitWidth, /*isPackedStorage=*/clEnableI1Support);
35+
}
36+
37+
bool needToPackSubByteElementBitWidth(unsigned bitWidth, bool isPackedStorage) {
2738
// Enable i1 support if requested.
28-
if (clEnableI1Support && bitWidth == 1) {
39+
if (isPackedStorage && bitWidth == 1) {
2940
return true;
3041
}
3142
// Require the original bit width to be some power of two for now to avoid
@@ -37,18 +48,28 @@ bool needToPackSubByteElementBitWidth(unsigned bitWidth) {
3748

3849
bool needToPackSubByteElements(RankedTensorType shapedType) {
3950
unsigned bitWidth = IREE::Util::getTypeBitWidth(shapedType.getElementType());
40-
return needToPackSubByteElementBitWidth(bitWidth);
51+
// Two paths to enable packed storage for i1 tensors: the attribute or cl
52+
// option. The cl option will be dropped once frontend supports emitting
53+
// tensors with attributes.
54+
bool isPackedStorage =
55+
IREE::Encoding::hasPackedStorageAttr(shapedType) || clEnableI1Support;
56+
return needToPackSubByteElementBitWidth(bitWidth, isPackedStorage);
4157
}
4258

4359
Type legalizeStorageElementType(Type elementType) {
60+
return legalizeStorageElementType(elementType,
61+
/*isPackedStorage=*/clEnableI1Support);
62+
}
63+
64+
Type legalizeStorageElementType(Type elementType, bool isPackedStorage) {
4465
// Only handle integers; floats in MLIR all have aligned widths (today).
4566
auto intType = dyn_cast<IntegerType>(elementType);
4667
if (!intType)
4768
return elementType;
4869

4970
// For sub-byte elements, default to pack them into bytes.
5071
unsigned bitWidth = intType.getWidth();
51-
if (needToPackSubByteElementBitWidth(bitWidth))
72+
if (needToPackSubByteElementBitWidth(bitWidth, isPackedStorage))
5273
return elementType;
5374

5475
// Otherwise, extend them to the next power-of-two bit width.
@@ -72,13 +93,16 @@ Value calculateStorageElementCountInBytes(Location loc,
7293
loc, builder, shapedType, dynamicDims);
7394
}
7495

96+
// TODO: remove cl options once frontend can emit packed i1 tensors.
97+
bool isPackedStorage =
98+
IREE::Encoding::hasPackedStorageAttr(shapedType) || clEnableI1Support;
7599
Type alignedElementType =
76-
legalizeStorageElementType(shapedType.getElementType());
100+
legalizeStorageElementType(shapedType.getElementType(), isPackedStorage);
77101
unsigned elementBits = IREE::Util::getTypeBitWidth(alignedElementType);
78102

79103
// Calculate all static dims first, if any.
80104
int64_t staticCount = 1;
81-
if (!needToPackSubByteElementBitWidth(elementBits)) {
105+
if (!needToPackSubByteElementBitWidth(elementBits, isPackedStorage)) {
82106
staticCount *= IREE::Util::getRoundedElementByteWidth(alignedElementType);
83107
}
84108

@@ -93,13 +117,13 @@ Value calculateStorageElementCountInBytes(Location loc,
93117
value = builder.createOrFold<arith::MulIOp>(loc, value, dim);
94118
}
95119
// Sub-byte packing requires putting multiple elements in the same byte.
96-
if (needToPackSubByteElementBitWidth(elementBits)) {
120+
if (needToPackSubByteElementBitWidth(elementBits, isPackedStorage)) {
97121
assert(8 % elementBits == 0);
98122
unsigned byteElements = 8 / elementBits;
99123
// TODO(antiagainst): We may want to emit runtime check to make sure this is
100124
// divisible.
101125
auto divisor = builder.create<arith::ConstantIndexOp>(loc, byteElements);
102-
if (!clEnableI1Support && dynamicDims.empty() &&
126+
if (!isPackedStorage && dynamicDims.empty() &&
103127
(staticCount * elementBits) % 8 != 0) {
104128
return nullptr;
105129
}
@@ -113,12 +137,15 @@ Value calculateStorageElementOffsetInBytes(Location loc,
113137
RankedTensorType originalType,
114138
Value linearizedIndex,
115139
OpBuilder &builder) {
116-
Type alignedElementType =
117-
legalizeStorageElementType(originalType.getElementType());
140+
// TODO: remove cl options once frontend can emit packed i1 tensors.
141+
bool isPackedStorage =
142+
IREE::Encoding::hasPackedStorageAttr(originalType) || clEnableI1Support;
143+
Type alignedElementType = legalizeStorageElementType(
144+
originalType.getElementType(), isPackedStorage);
118145
unsigned elementBits = IREE::Util::getTypeBitWidth(alignedElementType);
119146

120147
// Sub-byte packing requires putting multiple elements in the same byte.
121-
if (needToPackSubByteElementBitWidth(elementBits)) {
148+
if (needToPackSubByteElementBitWidth(elementBits, isPackedStorage)) {
122149
Value byteElements =
123150
builder.create<arith::ConstantIndexOp>(loc, 8 / elementBits);
124151
// TODO(antiagainst): We may want to emit runtime check to make sure this is

compiler/src/iree/compiler/Utils/ElementPackingUtils.h

+11
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ namespace mlir::iree_compiler {
1515

1616
/// Returns true if the given |bitWidth|, if appearing at runtime-kernel
1717
/// interface, is less than a byte that should be tightly packed together.
18+
bool needToPackSubByteElementBitWidth(unsigned bitWidth, bool isPackedStorage);
19+
20+
/// Temporary wrapper for the above function. `isPackedStorage` will be
21+
/// determined by the cl option. This allows enabling packed storage for i1
22+
/// in both attribute and cl option ways.
1823
bool needToPackSubByteElementBitWidth(unsigned bitWidth);
24+
1925
/// Returns true if the given |shapedType|, if appearing at runtime-kernel
2026
/// interface, has sub-byte element types that should be tightly packed
2127
/// together.
@@ -27,6 +33,11 @@ bool needToPackSubByteElements(RankedTensorType shapedType);
2733
/// runtime and kernel. For such cases, we perform tight packing for supported
2834
/// sub-byte elements, and expand to the next power-of-two bit width for other
2935
/// cases.
36+
Type legalizeStorageElementType(Type elementType, bool isPackedStorage);
37+
38+
/// Temporary wrapper for the above function. `isPackedStorage` will be
39+
/// determined by the cl option. This allows enabling packed storage for i1
40+
/// in both attribute and cl option ways.
3041
Type legalizeStorageElementType(Type elementType);
3142

3243
/// Emits IR with the given |builder| to calculate the total number of bytes

0 commit comments

Comments
 (0)