Merge pull request #6765 from google/benvanik-hal-encoding

This adds partial compiler and runtime support for passing around encodings.

Progress on #6762.
diff --git a/bindings/python/iree/runtime/hal.h b/bindings/python/iree/runtime/hal.h
index d25c5a6..25a1ff3 100644
--- a/bindings/python/iree/runtime/hal.h
+++ b/bindings/python/iree/runtime/hal.h
@@ -100,9 +100,11 @@
     iree_hal_buffer_view_t* bv;
     iree_hal_element_type_t element_type = iree_hal_make_element_type(
         IREE_HAL_ELEMENT_TYPE_NONE, element_size * 8);
+    iree_hal_encoding_type_t encoding_type =
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
     CheckApiStatus(
         iree_hal_buffer_view_create(raw_ptr(), shape.s.data(), shape.s.size(),
-                                    element_type, &bv),
+                                    element_type, encoding_type, &bv),
         "Error creating buffer view");
     return HalBufferView::CreateRetained(bv);
   }
diff --git a/bindings/python/iree/runtime/vm.cc b/bindings/python/iree/runtime/vm.cc
index 864bf5d..a9152c5 100644
--- a/bindings/python/iree/runtime/vm.cc
+++ b/bindings/python/iree/runtime/vm.cc
@@ -238,13 +238,16 @@
                        "Dependent buffer arguments not implemented");
   }
 
+  iree_hal_encoding_type_t encoding_type =
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
+
   // Create the buffer_view. (note that numpy shape is ssize_t)
   std::vector<int> dims(py_view.ndim);
   std::copy(py_view.shape, py_view.shape + py_view.ndim, dims.begin());
   iree_hal_buffer_view_t* buffer_view;
   CheckApiStatus(
       iree_hal_buffer_view_create(raw_buffer, dims.data(), dims.size(),
-                                  element_type, &buffer_view),
+                                  element_type, encoding_type, &buffer_view),
       "Error allocating buffer_view");
   iree_hal_buffer_release(raw_buffer);
   iree_vm_ref_t buffer_view_ref = iree_hal_buffer_view_move_ref(buffer_view);
diff --git a/bindings/tflite/tensor.c b/bindings/tflite/tensor.c
index 8633a2d..1fdb16d 100644
--- a/bindings/tflite/tensor.c
+++ b/bindings/tflite/tensor.c
@@ -122,8 +122,9 @@
   }
   iree_device_size_t allocation_size = 0;
   IREE_RETURN_AND_END_ZONE_IF_ERROR(
-      z0, iree_hal_buffer_compute_view_size(shape_dims, tensor->shape_rank,
-                                            element_type, &allocation_size));
+      z0, iree_hal_buffer_compute_view_size(
+              shape_dims, tensor->shape_rank, element_type,
+              IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, &allocation_size));
   allocation_size *= storage_scalar;
 
   // If the old buffer is the same size then no need to realloc.
diff --git a/iree/compiler/Bindings/Native/Transforms/test/wrap_entry_points.mlir b/iree/compiler/Bindings/Native/Transforms/test/wrap_entry_points.mlir
index bcf4343..9ab791a 100644
--- a/iree/compiler/Bindings/Native/Transforms/test/wrap_entry_points.mlir
+++ b/iree/compiler/Bindings/Native/Transforms/test/wrap_entry_points.mlir
@@ -7,9 +7,9 @@
 //  CHECK-SAME: ) attributes {
 //  CHECK-SAME:   iree.abi.stub
 //  CHECK-SAME: } {
-//  CHECK-NEXT:   %[[ARG0_DIM0:.+]] = hal.buffer_view.dim %[[ARG0]], 0 : index
+//  CHECK-NEXT:   %[[ARG0_DIM0:.+]] = hal.buffer_view.dim<%[[ARG0]] : !hal.buffer_view>[0] : index
 //  CHECK-NEXT:   %[[ARG0_TENSOR:.+]] = hal.tensor.cast %[[ARG0]] : !hal.buffer_view -> tensor<?x8x8x3xf32>{%[[ARG0_DIM0]]}
-//  CHECK-NEXT:   %[[ARG1_DIM0:.+]] = hal.buffer_view.dim %[[ARG1]], 0 : index
+//  CHECK-NEXT:   %[[ARG1_DIM0:.+]] = hal.buffer_view.dim<%[[ARG1]] : !hal.buffer_view>[0] : index
 //  CHECK-NEXT:   %[[ARG1_TENSOR:.+]] = hal.tensor.cast %[[ARG1]] : !hal.buffer_view -> tensor<?x8x8x3xf32>{%[[ARG1_DIM0]]}
 //  CHECK-NEXT:   %[[RET_TENSOR:.+]]:2 = call @_dynamicEntry(%[[ARG0_TENSOR]], %[[ARG1_TENSOR]])
 //       CHECK:   %[[RET0_DIM0:.+]] = tensor.dim %[[RET_TENSOR]]#0, %c0{{.*}} : tensor<?x8x8x3xf32>
diff --git a/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/ConvertStreamOps.cpp b/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/ConvertStreamOps.cpp
index 83d6d35..9db52a9 100644
--- a/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/ConvertStreamOps.cpp
+++ b/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/ConvertStreamOps.cpp
@@ -334,6 +334,8 @@
 
     auto elementType = getElementType(shapedType.getElementType(), builder);
     assert(elementType && "unhandled element type for allocation");
+    auto encodingType = getEncodingType({}, builder);
+    assert(encodingType && "unhandled encoding type for allocation");
 
     SmallVector<Value> shapeDims(shapedType.getRank());
     int64_t dynamicDimIndex = 0;
@@ -346,7 +348,7 @@
     }
 
     auto size = builder.createOrFold<IREE::HAL::AllocatorComputeSizeOp>(
-        loc, allocator(), shapeDims, elementType);
+        loc, allocator(), shapeDims, elementType, encodingType);
     if (shapedType.hasStaticShape()) {
       staticShapeToSizeMap[shapedType] = size;
     } else {
@@ -405,6 +407,17 @@
     return constantValue;
   }
 
+  Value getEncodingType(Attribute encodingType, OpBuilder &builder) {
+    auto it = memoizedEncodingTypesConstants.find(encodingType);
+    if (it != memoizedEncodingTypesConstants.end()) return it->second;
+    auto i32Value = IREE::HAL::getEncodingTypeValue(encodingType);
+    assert(i32Value.hasValue() && "unhandled encoding type for allocation");
+    auto constantValue =
+        builder.createOrFold<ConstantIntOp>(loc, i32Value.getValue(), 32);
+    memoizedEncodingTypesConstants[encodingType] = constantValue;
+    return constantValue;
+  }
+
   Location loc;
 
   // !hal.device used throughout the stream.
@@ -429,6 +442,9 @@
   // Small cache of constants used for element types.
   DenseMap<Type, Value> memoizedElementTypesConstants;
 
+  // Small cache of constants used for encoding types.
+  DenseMap<Attribute, Value> memoizedEncodingTypesConstants;
+
   // Map of static shaped types to computed size values.
   DenseMap<Type, Value> staticShapeToSizeMap;
 
diff --git a/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/global_ops.mlir b/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/global_ops.mlir
index cc107a0..f961fba 100644
--- a/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/global_ops.mlir
+++ b/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/global_ops.mlir
@@ -66,8 +66,8 @@
 // Checks that the implicit cast allowing a buffer_view to store into a variable
 // that maps to a buffer is permitted.
 // CHECK-LABEL: util.global public mutable @var_with_buffer_view_store
-// CHECK: %[[buffer:.*]] = hal.buffer_view.buffer %arg0 : !hal.buffer
-// CHECK: util.global.store %[[buffer]], @var_with_buffer_view_store : !hal.buffer
+// CHECK: %[[BUFFER:.*]] = hal.buffer_view.buffer<%arg0 : !hal.buffer_view> : !hal.buffer
+// CHECK: util.global.store %[[BUFFER]], @var_with_buffer_view_store : !hal.buffer
 util.global public mutable @var_with_buffer_view_store = dense<0.000000e+00> : tensor<f32>
 func @fn(%arg0: !hal.buffer_view) {
   %0 = hal.tensor.cast %arg0 : !hal.buffer_view -> tensor<f32>
@@ -79,8 +79,8 @@
 // Checks that stores are permitted for variables that do not dominate the
 // function containing a store.
 // CHECK-LABEL: func @store_var_out_of_order
-// CHECK: %[[buffer:.*]] = hal.buffer_view.buffer %arg0 : !hal.buffer
-// CHECK: util.global.store %[[buffer]], @var_out_of_order : !hal.buffer
+// CHECK: %[[BUFFER:.*]] = hal.buffer_view.buffer<%arg0 : !hal.buffer_view> : !hal.buffer
+// CHECK: util.global.store %[[BUFFER]], @var_out_of_order : !hal.buffer
 func @store_var_out_of_order(%arg0: !hal.buffer_view) {
   %0 = hal.tensor.cast %arg0 : !hal.buffer_view -> tensor<f32>
   util.global.store %0, @var_out_of_order : tensor<f32>
@@ -92,9 +92,9 @@
 // Checks that the implicit cast allowing a buffer_view to indirect store into
 // a variable that maps to a buffer is permitted.
 // CHECK-LABEL: util.global public mutable @var_indirect_with_buffer_view_store
-// CHECK: %[[ptr:.*]] = util.global.address @var_indirect_with_buffer_view_store : !util.ptr<!hal.buffer>
-// CHECK: %[[buffer:.*]] = hal.buffer_view.buffer %arg0 : !hal.buffer
-// CHECK: util.global.store.indirect %[[buffer]], %[[ptr]] : !hal.buffer -> !util.ptr<!hal.buffer>
+// CHECK: %[[PTR:.*]] = util.global.address @var_indirect_with_buffer_view_store : !util.ptr<!hal.buffer>
+// CHECK: %[[BUFFER:.*]] = hal.buffer_view.buffer<%arg0 : !hal.buffer_view> : !hal.buffer
+// CHECK: util.global.store.indirect %[[BUFFER]], %[[PTR]] : !hal.buffer -> !util.ptr<!hal.buffer>
 util.global public mutable @var_indirect_with_buffer_view_store : tensor<i32>
 func @fn(%arg0: !hal.buffer_view) {
   %0 = util.global.address @var_indirect_with_buffer_view_store : !util.ptr<tensor<i32>>
diff --git a/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/stream_ops.mlir b/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/stream_ops.mlir
index be08dbe..ccfaf18 100644
--- a/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/stream_ops.mlir
+++ b/iree/compiler/Dialect/HAL/Conversion/FlowToHAL/test/stream_ops.mlir
@@ -214,7 +214,7 @@
 // There is nothing to verify here except that correct IR is generated (if
 // constants are not handled properly, it will produce illegally ordered IR).
 func @tensorReshapeWithTiedConstant(%arg0: !hal.buffer_view) -> !hal.buffer_view {
-  %0 = hal.buffer_view.dim %arg0, 0 : index
+  %0 = hal.buffer_view.dim<%arg0 : !hal.buffer_view>[0] : index
   %1 = hal.tensor.cast %arg0 : !hal.buffer_view -> tensor<?xf32>{%0}
   %2 = flow.ex.stream.fragment(%1) : (tensor<?xf32>{%0}) -> tensor<4xf32> =
       (%arg1: tensor<?xf32>) -> tensor<4xf32> {
diff --git a/iree/compiler/Dialect/HAL/Conversion/HALToVM/ConvertBufferViewOps.cpp b/iree/compiler/Dialect/HAL/Conversion/HALToVM/ConvertBufferViewOps.cpp
index 16fb3cd..32d9de2 100644
--- a/iree/compiler/Dialect/HAL/Conversion/HALToVM/ConvertBufferViewOps.cpp
+++ b/iree/compiler/Dialect/HAL/Conversion/HALToVM/ConvertBufferViewOps.cpp
@@ -24,6 +24,8 @@
       context, importSymbols, typeConverter, "hal.buffer_view.byte_length");
   patterns.insert<VMImportOpConversion<IREE::HAL::BufferViewElementTypeOp>>(
       context, importSymbols, typeConverter, "hal.buffer_view.element_type");
+  patterns.insert<VMImportOpConversion<IREE::HAL::BufferViewEncodingTypeOp>>(
+      context, importSymbols, typeConverter, "hal.buffer_view.encoding_type");
   patterns.insert<VMImportOpConversion<IREE::HAL::BufferViewRankOp>>(
       context, importSymbols, typeConverter, "hal.buffer_view.rank");
   patterns.insert<VMImportOpConversion<IREE::HAL::BufferViewDimOp>>(
diff --git a/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/allocator_ops.mlir b/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/allocator_ops.mlir
index 5fc4a46..1dd1bba 100644
--- a/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/allocator_ops.mlir
+++ b/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/allocator_ops.mlir
@@ -5,8 +5,10 @@
   // CHECK: %c4194304 = vm.const.i32 4194304 : i32
   // CHECK-NOT: hal.allocator.compute_size
   %c1024 = constant 1024 : index
+  %c1_i32 = constant 1 : i32
   %c32_i32 = constant 32 : i32
-  %0 = hal.allocator.compute_size<%arg0 : !hal.allocator> shape([%c1024, %c1024]) type(%c32_i32) : index
+  %0 = hal.allocator.compute_size<%arg0 : !hal.allocator>
+      shape([%c1024, %c1024]) type(%c32_i32) encoding(%c1_i32) : index
   return %0 : index
 }
 
diff --git a/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/buffer_view_ops.mlir b/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/buffer_view_ops.mlir
index e32cc72..f11b2e8 100644
--- a/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/buffer_view_ops.mlir
+++ b/iree/compiler/Dialect/HAL/Conversion/HALToVM/test/buffer_view_ops.mlir
@@ -6,9 +6,9 @@
   // CHECK-DAG: %[[D0:.+]] = vm.call @hal.buffer_view.dim(%[[VIEW]], %zero)
   // CHECK-DAG: %[[D1:.+]] = vm.call @hal.buffer_view.dim(%[[VIEW]], %c1)
   // CHECK-DAG: %[[D2:.+]] = vm.call @hal.buffer_view.dim(%[[VIEW]], %c2)
-  %0 = hal.buffer_view.dim %arg0, 0 : index
-  %1 = hal.buffer_view.dim %arg0, 1 : index
-  %2 = hal.buffer_view.dim %arg0, 2 : index
+  %0 = hal.buffer_view.dim<%arg0 : !hal.buffer_view>[0] : index
+  %1 = hal.buffer_view.dim<%arg0 : !hal.buffer_view>[1] : index
+  %2 = hal.buffer_view.dim<%arg0 : !hal.buffer_view>[2] : index
   // CHECK-NEXT: vm.return %[[D0]], %[[D1]], %[[D2]]
   return %0, %1, %2 : index, index, index
 }
diff --git a/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/ConvertIREEToHAL.cpp b/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/ConvertIREEToHAL.cpp
index da50553..c50c619 100644
--- a/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/ConvertIREEToHAL.cpp
+++ b/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/ConvertIREEToHAL.cpp
@@ -48,6 +48,11 @@
     if (!elementType.hasValue()) {
       return rewriter.notifyMatchFailure(constantOp, "unhandled element type");
     }
+    // TODO(#6762): get encoding type.
+    auto encodingType = IREE::HAL::getEncodingTypeValue({});
+    if (!encodingType.hasValue()) {
+      return rewriter.notifyMatchFailure(constantOp, "unhandled encoding type");
+    }
 
     auto buffer = rewriter.createOrFold<IREE::HAL::AllocatorConstantOp>(
         constantOp.getLoc(), IREE::HAL::BufferType::get(rewriter.getContext()),
@@ -62,7 +67,8 @@
     }
 
     auto view = rewriter.createOrFold<IREE::HAL::BufferViewCreateOp>(
-        constantOp.getLoc(), buffer, elementType.getValue(), shape);
+        constantOp.getLoc(), buffer, elementType.getValue(),
+        encodingType.getValue(), shape);
 
     rewriter.replaceOpWithNewOp<IREE::Util::DoNotOptimizeOp>(constantOp, view);
     return success();
diff --git a/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/test/shape_constants.mlir b/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/test/shape_constants.mlir
index a043e26..3ba7c69 100644
--- a/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/test/shape_constants.mlir
+++ b/iree/compiler/Dialect/HAL/Conversion/IREEToHAL/test/shape_constants.mlir
@@ -7,7 +7,11 @@
   // CHECK-SAME:   type("HostVisible|DeviceVisible|DeviceLocal")
   // CHECK-SAME:   usage("Constant|Transfer|Mapping|Dispatch")
   // CHECK-SAME:   : !hal.buffer = dense<2> : tensor<2xi32>
-  //      CHECK: %[[VIEW:.+]] = hal.buffer_view.create %[[BUFFER]], element_type = %c16777248_i32, shape = [%c2] : !hal.buffer -> !hal.buffer_view
+  //      CHECK: %[[VIEW:.+]] = hal.buffer_view.create
+  // CHECK-SAME:   buffer(%[[BUFFER]] : !hal.buffer)
+  // CHECK-SAME:   shape([%c2])
+  // CHECK-SAME:   type(%c16777248_i32)
+  // CHECK-SAME:   encoding(%c1_i32) : !hal.buffer_view
   // CHECK-NEXT: %[[RET:.+]] = util.do_not_optimize(%[[VIEW]]) : !hal.buffer_view
   %c = util.dynamic_shape_constant dense<2> : tensor<2xi32> -> tensor<?xi32>
   return
diff --git a/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/ConvertStandardToHAL.cpp b/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/ConvertStandardToHAL.cpp
index e04b393..2eb1ac7 100644
--- a/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/ConvertStandardToHAL.cpp
+++ b/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/ConvertStandardToHAL.cpp
@@ -57,7 +57,7 @@
                                       newOperands.source_dims());
         newValue = rewriter.create<IREE::HAL::BufferViewCreateOp>(
             op.getLoc(), adaptor.getBuffer(), adaptor.getElementType(),
-            shapeDims);
+            adaptor.getEncodingType(), shapeDims);
       } else {
         newValue = adaptor.getBufferView();
       }
diff --git a/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/test/structural_ops.mlir b/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/test/structural_ops.mlir
index 01665d1..ef6460a 100644
--- a/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/test/structural_ops.mlir
+++ b/iree/compiler/Dialect/HAL/Conversion/StandardToHAL/test/structural_ops.mlir
@@ -37,10 +37,10 @@
     // CHECK: %[[C1_1:.*]] = constant 1 : index
     // CHECK: %[[C4:.*]] = constant 4 : index
     // CHECK: %[[C1_2:.*]] = constant 1 : index
-    // CHECK: %[[ET:.*]] = constant {{.*}} : i32
+    // CHECK: %[[ET:.*]] = constant 50331680 : i32
     // CHECK: %[[VIEW:.*]] = hal.buffer_view.create
-    //   CHECK-SAME: element_type = %[[ET]],
-    //   CHECK-SAME: shape = [%[[C3]], %[[C2]], %[[C1_1]], %[[C4]], %[[C1_2]]]
+    //   CHECK-SAME: shape([%[[C3]], %[[C2]], %[[C1_1]], %[[C4]], %[[C1_2]]])
+    //   CHECK-SAME: type(%[[ET]])
     // CHECK: return %[[VIEW]]
     %2 = hal.tensor.cast %1 : tensor<3x2x1x4x1xf32> -> !hal.buffer_view
     return %2 : !hal.buffer_view
diff --git a/iree/compiler/Dialect/HAL/IR/HALBase.td b/iree/compiler/Dialect/HAL/IR/HALBase.td
index 9610e29..e6c37fb 100644
--- a/iree/compiler/Dialect/HAL/IR/HALBase.td
+++ b/iree/compiler/Dialect/HAL/IR/HALBase.td
@@ -355,6 +355,10 @@
 def HAL_ElementTypeAttr : SignlessIntegerAttrBase<
   I32, "element type attribute">;
 
+def HAL_EncodingType : TypeAlias<I32>;
+def HAL_EncodingTypeAttr : SignlessIntegerAttrBase<
+  I32, "encoding type attribute">;
+
 def HAL_DeviceSize : TypeAlias<Index>;
 def HAL_DeviceSizeAttr : Util_IndexAttrBase<"iree_device_size_t">;
 def HAL_DeviceSizes : Variadic<HAL_DeviceSize>;
diff --git a/iree/compiler/Dialect/HAL/IR/HALOpFolders.cpp b/iree/compiler/Dialect/HAL/IR/HALOpFolders.cpp
index 312ead6..f405b58 100644
--- a/iree/compiler/Dialect/HAL/IR/HALOpFolders.cpp
+++ b/iree/compiler/Dialect/HAL/IR/HALOpFolders.cpp
@@ -60,6 +60,8 @@
     // TODO(benvanik): use buffer constraints for alignment.
     BufferConstraintsAdaptor bufferConstraints(op.getLoc(), op.allocator());
 
+    // TODO(#6762): switch based on op.encoding().
+
     auto elementSize =
         getElementByteCount(op.getLoc(), op.element_type(), rewriter);
     auto byteSize =
@@ -89,6 +91,8 @@
     // TODO(benvanik): use buffer constraints.
     BufferConstraintsAdaptor bufferConstraints(op.getLoc(), op.allocator());
 
+    // TODO(#6762): switch based on op.encoding().
+
     auto offset = rewriter.createOrFold<mlir::ConstantIndexOp>(op.getLoc(), 0);
     for (size_t i = 0; i < op.indices().size(); ++i) {
       // TODO(benvanik): check error case in debug builds.
@@ -146,10 +150,10 @@
 
     auto startByteOffset = rewriter.createOrFold<AllocatorComputeOffsetOp>(
         op.getLoc(), rewriter.getIndexType(), op.allocator(), op.shape(),
-        op.element_type(), op.indices());
+        op.element_type(), op.encoding_type(), op.indices());
     auto endByteOffset = rewriter.createOrFold<AllocatorComputeOffsetOp>(
         op.getLoc(), rewriter.getIndexType(), op.allocator(), op.shape(),
-        op.element_type(), endIndices);
+        op.element_type(), op.encoding_type(), endIndices);
 
     auto elementSize =
         getElementByteCount(op.getLoc(), op.element_type(), rewriter);
@@ -186,6 +190,11 @@
     if (!elementType.hasValue()) {
       return rewriter.notifyMatchFailure(op, "unhandled element type");
     }
+    // TODO(#6762): get encoding type.
+    auto encodingType = IREE::HAL::getEncodingTypeValue({});
+    if (!encodingType.hasValue()) {
+      return rewriter.notifyMatchFailure(op, "unhandled encoding type");
+    }
 
     // TODO(benvanik): compute from SSA use-def chain uses.
     IREE::HAL::MemoryTypeBitfield memoryTypes =
@@ -215,7 +224,8 @@
         }
       }
       auto bufferView = rewriter.createOrFold<BufferViewCreateOp>(
-          op.getLoc(), deviceBuffer, elementType.getValue(), shape);
+          op.getLoc(), deviceBuffer, elementType.getValue(),
+          encodingType.getValue(), shape);
       rewriter.replaceOp(op, {bufferView});
     } else {
       rewriter.replaceOp(op, {deviceBuffer});
diff --git a/iree/compiler/Dialect/HAL/IR/HALOps.cpp b/iree/compiler/Dialect/HAL/IR/HALOps.cpp
index 7e3fb55..edb6f90 100644
--- a/iree/compiler/Dialect/HAL/IR/HALOps.cpp
+++ b/iree/compiler/Dialect/HAL/IR/HALOps.cpp
@@ -313,17 +313,19 @@
 
 void AllocatorComputeSizeOp::build(OpBuilder &builder, OperationState &state,
                                    Value allocator, ValueRange shape,
-                                   int32_t elementType) {
+                                   int32_t elementType, int32_t encodingType) {
   build(builder, state, allocator, shape,
-        builder.createOrFold<ConstantIntOp>(state.location, elementType, 32));
+        builder.createOrFold<ConstantIntOp>(state.location, elementType, 32),
+        builder.createOrFold<ConstantIntOp>(state.location, encodingType, 32));
 }
 
 void AllocatorComputeSizeOp::build(OpBuilder &builder, OperationState &state,
                                    Value allocator, ValueRange shape,
-                                   Value elementType) {
+                                   Value elementType, Value encodingType) {
   state.addOperands({allocator});
   state.addOperands(shape);
   state.addOperands(elementType);
+  state.addOperands(encodingType);
   state.addTypes({builder.getIndexType()});
 }
 
@@ -338,18 +340,22 @@
 
 void AllocatorComputeOffsetOp::build(OpBuilder &builder, OperationState &state,
                                      Value allocator, ValueRange shape,
-                                     int32_t elementType, ValueRange indices) {
+                                     int32_t elementType, int32_t encodingType,
+                                     ValueRange indices) {
   build(builder, state, allocator, shape,
         builder.createOrFold<ConstantIntOp>(state.location, elementType, 32),
+        builder.createOrFold<ConstantIntOp>(state.location, encodingType, 32),
         indices);
 }
 
 void AllocatorComputeOffsetOp::build(OpBuilder &builder, OperationState &state,
                                      Value allocator, ValueRange shape,
-                                     Value elementType, ValueRange indices) {
+                                     Value elementType, Value encodingType,
+                                     ValueRange indices) {
   state.addOperands({allocator});
   state.addOperands(shape);
   state.addOperands(elementType);
+  state.addOperands(encodingType);
   state.addOperands(indices);
   state.addTypes({builder.getIndexType()});
 }
@@ -365,20 +371,22 @@
 
 void AllocatorComputeRangeOp::build(OpBuilder &builder, OperationState &state,
                                     Value allocator, ValueRange shape,
-                                    int32_t elementType, ValueRange indices,
-                                    ValueRange lengths) {
+                                    int32_t elementType, int32_t encodingType,
+                                    ValueRange indices, ValueRange lengths) {
   build(builder, state, allocator, shape,
         builder.createOrFold<ConstantIntOp>(state.location, elementType, 32),
+        builder.createOrFold<ConstantIntOp>(state.location, encodingType, 32),
         indices, lengths);
 }
 
 void AllocatorComputeRangeOp::build(OpBuilder &builder, OperationState &state,
                                     Value allocator, ValueRange shape,
-                                    Value elementType, ValueRange indices,
-                                    ValueRange lengths) {
+                                    Value elementType, Value encodingType,
+                                    ValueRange indices, ValueRange lengths) {
   state.addOperands({allocator});
   state.addOperands(shape);
   state.addOperands(elementType);
+  state.addOperands(encodingType);
   state.addOperands(indices);
   state.addOperands(lengths);
   state.addTypes({builder.getIndexType(), builder.getIndexType()});
@@ -503,16 +511,17 @@
 
 void BufferViewCreateOp::build(OpBuilder &builder, OperationState &state,
                                Value buffer, int32_t elementType,
-                               ValueRange shape) {
+                               int32_t encodingType, ValueRange shape) {
   build(builder, state, buffer,
         builder.createOrFold<ConstantIntOp>(state.location, elementType, 32),
+        builder.createOrFold<ConstantIntOp>(state.location, encodingType, 32),
         shape);
 }
 
 void BufferViewCreateOp::build(OpBuilder &builder, OperationState &state,
                                Value buffer, Value elementType,
-                               ValueRange shape) {
-  state.addOperands({buffer, elementType});
+                               Value encodingType, ValueRange shape) {
+  state.addOperands({buffer, elementType, encodingType});
   state.addOperands(shape);
   state.addTypes({BufferViewType::get(builder.getContext())});
 }
diff --git a/iree/compiler/Dialect/HAL/IR/HALOps.td b/iree/compiler/Dialect/HAL/IR/HALOps.td
index 91cfbf3..2d87fe6 100644
--- a/iree/compiler/Dialect/HAL/IR/HALOps.td
+++ b/iree/compiler/Dialect/HAL/IR/HALOps.td
@@ -117,13 +117,15 @@
   let summary = [{buffer allocation size computation operation}];
   let description = [{
     Computes the byte size required for a buffer of the given shape and type.
-    This returns the same value as `hal.buffer_view.byte_length`.
+    The returned size may be larger than the logical size of the tensor type
+    stored within the buffer based on alignment requirements of the allocator.
   }];
 
   let arguments = (ins
     HAL_Allocator:$allocator,
     HAL_Shape:$shape,
-    HAL_ElementType:$element_type
+    HAL_ElementType:$element_type,
+    HAL_EncodingType:$encoding_type
   );
   let results = (outs
     HAL_DeviceSize:$result
@@ -133,15 +135,24 @@
     `<` $allocator `:` type($allocator) `>`
     `shape` `(` `[` $shape `]` `)`
     `type` `(` $element_type `)`
+    `encoding` `(` $encoding_type `)`
     `:` type($result)
     attr-dict-with-keyword
   }];
 
   let builders = [
-    OpBuilder<(ins "Value":$allocator, "ValueRange":$shape,
-      "int32_t":$elementType)>,
-    OpBuilder<(ins "Value":$allocator, "ValueRange":$shape,
-      "Value":$elementType)>,
+    OpBuilder<(ins
+      "Value":$allocator,
+      "ValueRange":$shape,
+      "int32_t":$elementType,
+      "int32_t":$encodingType
+    )>,
+    OpBuilder<(ins
+      "Value":$allocator,
+      "ValueRange":$shape,
+      "Value":$elementType,
+      "Value":$encodingType
+    )>,
   ];
 
   let hasCanonicalizer = 1;
@@ -160,6 +171,7 @@
     HAL_Allocator:$allocator,
     HAL_Shape:$shape,
     HAL_ElementType:$element_type,
+    HAL_EncodingType:$encoding_type,
     HAL_Dims:$indices
   );
 
@@ -172,15 +184,26 @@
     `indices` `(` `[` $indices `]` `)`
     `shape` `(` `[` $shape `]` `)`
     `type` `(` $element_type `)`
+    `encoding` `(` $encoding_type `)`
     `:` type($offset)
     attr-dict-with-keyword
   }];
 
   let builders = [
-    OpBuilder<(ins "Value":$allocator, "ValueRange":$shape,
-      "int32_t":$elementType, "ValueRange":$indices)>,
-    OpBuilder<(ins "Value":$allocator, "ValueRange":$shape,
-      "Value":$elementType, "ValueRange":$indices)>,
+    OpBuilder<(ins
+      "Value":$allocator,
+      "ValueRange":$shape,
+      "int32_t":$elementType,
+      "int32_t":$encodingType,
+      "ValueRange":$indices
+    )>,
+    OpBuilder<(ins
+      "Value":$allocator,
+      "ValueRange":$shape,
+      "Value":$elementType,
+      "Value":$encodingType,
+      "ValueRange":$indices
+    )>,
   ];
 
   let hasCanonicalizer = 1;
@@ -199,6 +222,7 @@
     HAL_Allocator:$allocator,
     HAL_Shape:$shape,
     HAL_ElementType:$element_type,
+    HAL_EncodingType:$encoding_type,
     HAL_Dims:$indices,
     HAL_Dims:$lengths
   );
@@ -214,15 +238,28 @@
     `lengths` `(` `[` $lengths `]` `)`
     `shape` `(` `[` $shape `]` `)`
     `type` `(` $element_type `)`
+    `encoding` `(` $encoding_type `)`
     `:` type($offset) `,` type($length)
     attr-dict-with-keyword
   }];
 
   let builders = [
-    OpBuilder<(ins "Value":$allocator, "ValueRange":$shape,
-      "int32_t":$elementType, "ValueRange":$indices, "ValueRange":$lengths)>,
-    OpBuilder<(ins "Value":$allocator, "ValueRange":$shape,
-      "Value":$elementType, "ValueRange":$indices, "ValueRange":$lengths)>,
+    OpBuilder<(ins
+      "Value":$allocator,
+      "ValueRange":$shape,
+      "int32_t":$elementType,
+      "int32_t":$encodingType,
+      "ValueRange":$indices,
+      "ValueRange":$lengths
+    )>,
+    OpBuilder<(ins
+      "Value":$allocator,
+      "ValueRange":$shape,
+      "Value":$elementType,
+      "Value":$encodingType,
+      "ValueRange":$indices,
+      "ValueRange":$lengths
+    )>,
   ];
 
   let hasCanonicalizer = 1;
@@ -569,6 +606,7 @@
   let arguments = (ins
     HAL_BufferType:$buffer,
     HAL_ElementType:$element_type,
+    HAL_EncodingType:$encoding_type,
     HAL_Shape:$shape
   );
   let results = (outs
@@ -576,10 +614,12 @@
   );
 
   let assemblyFormat = [{
-    $buffer `,`
-    `element_type` `=` $element_type `,`
-    `shape` `=` `[` $shape `]`
-    attr-dict `:` type($buffer) `->` type($result)
+    `buffer` `(` $buffer `:` type($buffer) `)`
+    `shape` `(` `[` $shape `]` `)`
+    `type` `(` $element_type `)`
+    `encoding` `(` $encoding_type `)`
+    `:` type($result)
+    attr-dict-with-keyword
   }];
 
   let skipDefaultBuilders = 1;
@@ -587,11 +627,13 @@
     OpBuilder<(ins
       "Value":$buffer,
       "int32_t":$elementType,
+      "int32_t":$encodingType,
       "ValueRange":$shape
     )>,
     OpBuilder<(ins
       "Value":$buffer,
       "Value":$elementType,
+      "Value":$encodingType,
       "ValueRange":$shape
     )>,
   ];
@@ -613,7 +655,9 @@
   );
 
   let assemblyFormat = [{
-    $buffer_view attr-dict `:` type($result)
+    `<` $buffer_view `:` type($buffer_view) `>`
+    `:` type($result)
+    attr-dict-with-keyword
   }];
 
   let hasCanonicalizer = 1;
@@ -638,7 +682,9 @@
   );
 
   let assemblyFormat = [{
-    $buffer_view attr-dict `:` type($result)
+    `<` $buffer_view `:` type($buffer_view) `>`
+    `:` type($result)
+    attr-dict-with-keyword
   }];
 
   let skipDefaultBuilders = 1;
@@ -661,7 +707,29 @@
   );
 
   let assemblyFormat = [{
-    $buffer_view attr-dict `:` type($result)
+    `<` $buffer_view `:` type($buffer_view) `>`
+    `:` type($result)
+    attr-dict-with-keyword
+  }];
+}
+
+def HAL_BufferViewEncodingTypeOp : HAL_PureOp<"buffer_view.encoding_type"> {
+  let summary = [{buffer view encoding type query}];
+  let description = [{
+    Returns the encoding type of the buffer view.
+  }];
+
+  let arguments = (ins
+    HAL_BufferView:$buffer_view
+  );
+  let results = (outs
+    HAL_EncodingType:$result
+  );
+
+  let assemblyFormat = [{
+    `<` $buffer_view `:` type($buffer_view) `>`
+    `:` type($result)
+    attr-dict-with-keyword
   }];
 }
 
@@ -679,7 +747,9 @@
   );
 
   let assemblyFormat = [{
-    $buffer_view attr-dict `:` type($result)
+    `<` $buffer_view `:` type($buffer_view) `>`
+    `:` type($result)
+    attr-dict-with-keyword
   }];
 }
 
@@ -698,7 +768,10 @@
   );
 
   let assemblyFormat = [{
-    $buffer_view `,` $index attr-dict `:` type($result)
+    `<` $buffer_view `:` type($buffer_view) `>`
+    `` `[` $index `]`
+    `:` type($result)
+    attr-dict-with-keyword
   }];
 }
 
@@ -716,7 +789,9 @@
   );
 
   let assemblyFormat = [{
-    $buffer_view attr-dict `:` type($result)
+    `<` $buffer_view `:` type($buffer_view) `>`
+    `:` type($result)
+    attr-dict-with-keyword
   }];
 
   let hasCanonicalizer = 1;
@@ -736,7 +811,8 @@
   );
 
   let assemblyFormat = [{
-    attr-dict ($operands^ `:` type($operands))?
+    $operands `:` type($operands)
+    attr-dict-with-keyword
   }];
 }
 
diff --git a/iree/compiler/Dialect/HAL/IR/HALTypes.cpp b/iree/compiler/Dialect/HAL/IR/HALTypes.cpp
index a4952ec..2f9c7b6 100644
--- a/iree/compiler/Dialect/HAL/IR/HALTypes.cpp
+++ b/iree/compiler/Dialect/HAL/IR/HALTypes.cpp
@@ -360,6 +360,20 @@
       c8);
 }
 
+llvm::Optional<int32_t> getEncodingTypeValue(Attribute attr) {
+  // TODO(#6762): encoding attribute handling/mapping to enums.
+  assert(!attr && "encoding types other than default not yet supported");
+  // Default to IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR for now.
+  return 1;
+}
+
+IntegerAttr getEncodingTypeAttr(Attribute attr, MLIRContext *context) {
+  auto encodingType = getEncodingTypeValue(attr);
+  if (!encodingType) return {};
+  return IntegerAttr::get(IntegerType::get(context, 32),
+                          encodingType.getValue());
+}
+
 //===----------------------------------------------------------------------===//
 // Size-aware type utils
 //===----------------------------------------------------------------------===//
diff --git a/iree/compiler/Dialect/HAL/IR/HALTypes.h b/iree/compiler/Dialect/HAL/IR/HALTypes.h
index d8d4608..a0abc18 100644
--- a/iree/compiler/Dialect/HAL/IR/HALTypes.h
+++ b/iree/compiler/Dialect/HAL/IR/HALTypes.h
@@ -57,6 +57,13 @@
 size_t getElementByteCount(IntegerAttr elementType);
 Value getElementByteCount(Location loc, Value elementType, OpBuilder &builder);
 
+// Returns a stable identifier for the MLIR encoding type or 0 (opaque) if the
+// type is unsupported in the ABI.
+llvm::Optional<int32_t> getEncodingTypeValue(Attribute attr);
+
+// Returns an attribute with the MLIR encoding type or 0 (opaque).
+IntegerAttr getEncodingTypeAttr(Attribute attr, MLIRContext *context);
+
 template <typename T>
 inline bool allEnumBitsSet(T value, T required) {
   return (static_cast<uint32_t>(value) & static_cast<uint32_t>(required)) ==
diff --git a/iree/compiler/Dialect/HAL/IR/test/allocator_op_folding.mlir b/iree/compiler/Dialect/HAL/IR/test/allocator_op_folding.mlir
index 2df6fb6..7f5cbb5 100644
--- a/iree/compiler/Dialect/HAL/IR/test/allocator_op_folding.mlir
+++ b/iree/compiler/Dialect/HAL/IR/test/allocator_op_folding.mlir
@@ -27,7 +27,11 @@
   // CHECK-SAME:   type("HostVisible|DeviceVisible|DeviceLocal")
   // CHECK-SAME:   usage("Constant|Transfer|Mapping|Dispatch")
   // CHECK-SAME:   : !hal.buffer
-  // CHECK-NEXT: %[[VIEW:.+]] = hal.buffer_view.create %[[BUFFER]], element_type = %c16777248_i32, shape = [%c4, %c4] : !hal.buffer -> !hal.buffer_view
+  // CHECK-NEXT: %[[VIEW:.+]] = hal.buffer_view.create
+  // CHECK-SAME:     buffer(%[[BUFFER]] : !hal.buffer)
+  // CHECK-SAME:     shape([%c4, %c4])
+  // CHECK-SAME:     type(%c16777248_i32)
+  // CHECK-SAME:     encoding(%c1_i32) : !hal.buffer_view
   %ref = hal.allocator.constant<%allocator : !hal.allocator>
          type(DeviceLocal) usage(Transfer) : !hal.buffer_view =
          dense<123> : tensor<4x4xi32>
diff --git a/iree/compiler/Dialect/HAL/IR/test/allocator_ops.mlir b/iree/compiler/Dialect/HAL/IR/test/allocator_ops.mlir
index 69d454f..4d6be0f 100644
--- a/iree/compiler/Dialect/HAL/IR/test/allocator_ops.mlir
+++ b/iree/compiler/Dialect/HAL/IR/test/allocator_ops.mlir
@@ -8,12 +8,16 @@
   %dim1 = constant 200 : index
   // CHECK-DAG: %[[TYPE:.+]] = constant 32
   %type = constant 32 : i32
+  // CHECK-DAG: %[[ENCODING:.+]] = constant 1
+  %encoding = constant 1 : i32
   // CHECK: %[[SIZE:.+]] = hal.allocator.compute_size<%arg0 : !hal.allocator>
   // CHECK-SAME:                                shape([%[[DIM0]], %[[DIM1]]])
-  // CHECK-SAME:                                 type(%[[TYPE]]) : index
+  // CHECK-SAME:                                 type(%[[TYPE]])
+  // CHECK-SAME:                             encoding(%[[ENCODING]]) : index
   %sz = hal.allocator.compute_size<%arg0 : !hal.allocator>
                              shape([%dim0, %dim1])
-                              type(%type) : index
+                              type(%type)
+                          encoding(%encoding) : index
   // CHECK-NEXT: return %[[SIZE]]
   return %sz : index
 }
@@ -32,14 +36,18 @@
   %dim1 = constant 200 : index
   // CHECK-DAG: %[[TYPE:.+]] = constant 32
   %type = constant 32 : i32
+  // CHECK-DAG: %[[ENCODING:.+]] = constant 1
+  %encoding = constant 1 : i32
   // CHECK: %[[OFFSET:.+]] = hal.allocator.compute_offset<%arg0 : !hal.allocator>
   // CHECK-SAME:                                  indices([%[[IDX0]], %[[IDX1]]])
   // CHECK-SAME:                                    shape([%[[DIM0]], %[[DIM1]]])
-  // CHECK-SAME:                                     type(%[[TYPE]]) : index
+  // CHECK-SAME:                                     type(%[[TYPE]])
+  // CHECK-SAME:                                 encoding(%[[ENCODING]]) : index
   %off = hal.allocator.compute_offset<%arg0 : !hal.allocator>
                               indices([%idx0, %idx1])
                                 shape([%dim0, %dim1])
-                                 type(%type) : index
+                                 type(%type)
+                             encoding(%encoding) : index
   // CHECK-NEXT: return %[[OFFSET]]
   return %off : index
 }
@@ -62,16 +70,20 @@
   %dim1 = constant 200 : index
   // CHECK-DAG: %[[TYPE:.+]] = constant 32
   %type = constant 32 : i32
+  // CHECK-DAG: %[[ENCODING:.+]] = constant 1
+  %encoding = constant 1 : i32
   // CHECK: = hal.allocator.compute_range<%arg0 : !hal.allocator>
   // CHECK-SAME:                  indices([%[[IDX0]], %[[IDX1]]])
   // CHECK-SAME:                  lengths([%[[LEN0]], %[[LEN1]]])
   // CHECK-SAME:                    shape([%[[DIM0]], %[[DIM1]]])
-  // CHECK-SAME:                     type(%[[TYPE]]) : index, index
+  // CHECK-SAME:                     type(%[[TYPE]])
+  // CHECK-SAME:                 encoding(%[[ENCODING]]) : index, index
   %off, %len = hal.allocator.compute_range<%arg0 : !hal.allocator>
                                    indices([%idx0, %idx1])
                                    lengths([%len0, %len1])
                                      shape([%dim0, %dim1])
-                                      type(%type) : index, index
+                                      type(%type)
+                                  encoding(%encoding) : index, index
   return %off, %len : index, index
 }
 
diff --git a/iree/compiler/Dialect/HAL/IR/test/buffer_view_folding.mlir b/iree/compiler/Dialect/HAL/IR/test/buffer_view_folding.mlir
index 802b0ae..6348da4 100644
--- a/iree/compiler/Dialect/HAL/IR/test/buffer_view_folding.mlir
+++ b/iree/compiler/Dialect/HAL/IR/test/buffer_view_folding.mlir
@@ -1,13 +1,17 @@
-// RUN: iree-opt -allow-unregistered-dialect -split-input-file -canonicalize -cse %s | iree-opt -allow-unregistered-dialect -split-input-file | IreeFileCheck %s
+// RUN: iree-opt -split-input-file -canonicalize -cse %s | iree-opt -allow-unregistered-dialect -split-input-file | IreeFileCheck %s
 
 // CHECK-LABEL: func @skip_buffer_view_buffer
 // CHECK-SAME: %[[BUFFER:.+]]: !hal.buffer
 func @skip_buffer_view_buffer(%buffer : !hal.buffer) -> !hal.buffer {
+  %c1 = constant 1 : i32
   %c10 = constant 10 : index
   %c11 = constant 11 : index
   %c32 = constant 32 : i32
-  %view = hal.buffer_view.create %buffer, element_type = %c32, shape = [%c10, %c11] : !hal.buffer -> !hal.buffer_view
-  %view_buffer = hal.buffer_view.buffer %view : !hal.buffer
+  %view = hal.buffer_view.create buffer(%buffer : !hal.buffer)
+                                 shape([%c10, %c11])
+                                 type(%c32)
+                                 encoding(%c1) : !hal.buffer_view
+  %view_buffer = hal.buffer_view.buffer<%view : !hal.buffer_view> : !hal.buffer
   // CHECK: return %[[BUFFER]]
   return %view_buffer : !hal.buffer
 }
@@ -16,11 +20,11 @@
 
 // CHECK-LABEL: func @expand_buffer_view_dims
 // CHECK-SAME: %[[VIEW:.+]]: !hal.buffer_view
-func @expand_buffer_view_dims(%arg0 : !hal.buffer_view) -> (index, index, index) {
-  // CHECK-DAG: %[[D0:.+]] = hal.buffer_view.dim %[[VIEW]], 0 : index
-  // CHECK-DAG: %[[D1:.+]] = hal.buffer_view.dim %[[VIEW]], 1 : index
-  // CHECK-DAG: %[[D2:.+]] = hal.buffer_view.dim %[[VIEW]], 2 : index
-  %0, %1, %2 = hal.buffer_view.dims %arg0 : index, index, index
+func @expand_buffer_view_dims(%view : !hal.buffer_view) -> (index, index, index) {
+  // CHECK-DAG: %[[D0:.+]] = hal.buffer_view.dim<%[[VIEW]] : !hal.buffer_view>[0] : index
+  // CHECK-DAG: %[[D1:.+]] = hal.buffer_view.dim<%[[VIEW]] : !hal.buffer_view>[1] : index
+  // CHECK-DAG: %[[D2:.+]] = hal.buffer_view.dim<%[[VIEW]] : !hal.buffer_view>[2] : index
+  %0, %1, %2 = hal.buffer_view.dims<%view : !hal.buffer_view> : index, index, index
   // CHECK-NEXT: return %[[D0]], %[[D1]], %[[D2]]
   return %0, %1, %2 : index, index, index
 }
diff --git a/iree/compiler/Dialect/HAL/IR/test/buffer_view_ops.mlir b/iree/compiler/Dialect/HAL/IR/test/buffer_view_ops.mlir
index d254350..a689425 100644
--- a/iree/compiler/Dialect/HAL/IR/test/buffer_view_ops.mlir
+++ b/iree/compiler/Dialect/HAL/IR/test/buffer_view_ops.mlir
@@ -1,11 +1,18 @@
-// RUN: iree-opt -allow-unregistered-dialect -split-input-file %s | iree-opt -allow-unregistered-dialect -split-input-file | IreeFileCheck %s
+// RUN: iree-opt -split-input-file %s | iree-opt -allow-unregistered-dialect -split-input-file | IreeFileCheck %s
 
 // CHECK-LABEL: @buffer_view_create
-func @buffer_view_create(%arg0: !hal.buffer) -> !hal.buffer_view {
+func @buffer_view_create(%arg0: !hal.buffer, %arg1: index, %arg2: index) -> !hal.buffer_view {
+  %c1 = constant 1 : i32
   %c32 = constant 32 : i32
-  %0:2 = "test_hal.shape"() : () -> (index, index)
-  // CHECK: %view = hal.buffer_view.create %arg0, element_type = %c32_i32, shape = [%0#0, %0#1] : !hal.buffer -> !hal.buffer_view
-  %view = hal.buffer_view.create %arg0, element_type = %c32, shape = [%0#0, %0#1] : !hal.buffer -> !hal.buffer_view
+  // CHECK: %view = hal.buffer_view.create
+  // CHECK-SAME: buffer(%arg0 : !hal.buffer)
+  // CHECK-SAME: shape([%arg1, %arg2])
+  // CHECK-SAME: type(%c32_i32)
+  // CHECK-SAME: encoding(%c1_i32) : !hal.buffer_view
+  %view = hal.buffer_view.create buffer(%arg0 : !hal.buffer)
+                                 shape([%arg1, %arg2])
+                                 type(%c32)
+                                 encoding(%c1) : !hal.buffer_view
   return %view : !hal.buffer_view
 }
 
@@ -13,8 +20,8 @@
 
 // CHECK-LABEL: @buffer_view_buffer
 func @buffer_view_buffer(%arg0: !hal.buffer_view) -> !hal.buffer {
-  // CHECK: %buffer = hal.buffer_view.buffer %arg0 : !hal.buffer
-  %buffer = hal.buffer_view.buffer %arg0 : !hal.buffer
+  // CHECK: %buffer = hal.buffer_view.buffer<%arg0 : !hal.buffer_view> : !hal.buffer
+  %buffer = hal.buffer_view.buffer<%arg0 : !hal.buffer_view> : !hal.buffer
   return %buffer : !hal.buffer
 }
 
@@ -22,8 +29,8 @@
 
 // CHECK-LABEL: @buffer_view_byte_length
 func @buffer_view_byte_length(%arg0: !hal.buffer_view) -> index {
-  // CHECK: %len = hal.buffer_view.byte_length %arg0 : index
-  %len = hal.buffer_view.byte_length %arg0 : index
+  // CHECK: %len = hal.buffer_view.byte_length<%arg0 : !hal.buffer_view> : index
+  %len = hal.buffer_view.byte_length<%arg0 : !hal.buffer_view> : index
   return %len : index
 }
 
@@ -31,11 +38,11 @@
 
 // CHECK-LABEL: @buffer_view_shape_queries
 func @buffer_view_shape_queries(%arg0: !hal.buffer_view) -> (index, index, index, index) {
-  // CHECK: %{{.+}} = hal.buffer_view.rank %arg0 : index
-  %0 = hal.buffer_view.rank %arg0 : index
-  // CHECK: %{{.+}} = hal.buffer_view.dim %arg0, 0 : index
-  %1 = hal.buffer_view.dim %arg0, 0 : index
-  // CHECK: %{{.+}}:2 = hal.buffer_view.dims %arg0 : index, index
-  %2, %3 = hal.buffer_view.dims %arg0 : index, index
+  // CHECK: %{{.+}} = hal.buffer_view.rank<%arg0 : !hal.buffer_view> : index
+  %0 = hal.buffer_view.rank<%arg0 : !hal.buffer_view> : index
+  // CHECK: %{{.+}} = hal.buffer_view.dim<%arg0 : !hal.buffer_view>[0] : index
+  %1 = hal.buffer_view.dim<%arg0 : !hal.buffer_view>[0] : index
+  // CHECK: %{{.+}}:2 = hal.buffer_view.dims<%arg0 : !hal.buffer_view> : index, index
+  %2, %3 = hal.buffer_view.dims<%arg0 : !hal.buffer_view> : index, index
   return %0, %1, %2, %3 : index, index, index, index
 }
diff --git a/iree/compiler/Dialect/HAL/Utils/TypeUtils.cpp b/iree/compiler/Dialect/HAL/Utils/TypeUtils.cpp
index 36e091c..ebe1e99 100644
--- a/iree/compiler/Dialect/HAL/Utils/TypeUtils.cpp
+++ b/iree/compiler/Dialect/HAL/Utils/TypeUtils.cpp
@@ -83,12 +83,19 @@
   auto elementType = IREE::HAL::getElementTypeValue(
       value.getType().cast<ShapedType>().getElementType());
   if (!elementType) return {};
+
+  // TODO(#6762): get encoding type from value.
+  auto encodingType = IREE::HAL::getEncodingTypeValue({});
+  if (!encodingType) return {};
+
   auto shape = IREE::HAL::getShapeDims(loc, value, builder);
   if (!shape) return {};
+
   auto allocatorValue = builder.createOrFold<IREE::HAL::BufferAllocatorOp>(
       loc, IREE::HAL::AllocatorType::get(builder.getContext()), value);
   return builder.createOrFold<IREE::HAL::AllocatorComputeSizeOp>(
-      loc, allocatorValue, *shape, elementType.getValue());
+      loc, allocatorValue, *shape, elementType.getValue(),
+      encodingType.getValue());
 }
 
 // static
@@ -122,6 +129,7 @@
       loc, oldValue, newValue, rewriter)));
   return TensorRewriteAdaptor(loc, oldValue, newValue, rewriter);
 }
+
 // static
 llvm::Optional<TensorRewriteAdaptor> TensorRewriteAdaptor::getChecked(
     Location loc, Value oldValue, Value newValue,
@@ -162,7 +170,7 @@
     auto shapeDims = getShapeDims();
     if (!shapeDims) return {};
     return rewriter_.createOrFold<IREE::HAL::BufferViewCreateOp>(
-        loc_, newValue_, getElementType(), *shapeDims);
+        loc_, newValue_, getElementType(), getEncodingType(), *shapeDims);
   }
 }
 
@@ -179,6 +187,15 @@
   return IREE::HAL::getElementTypeAttr(getTensorType().getElementType());
 }
 
+int32_t TensorRewriteAdaptor::getEncodingType() {
+  return (int32_t)getEncodingTypeAttr().getValue().getZExtValue();
+}
+
+IntegerAttr TensorRewriteAdaptor::getEncodingTypeAttr() {
+  // TODO(#6762): get encoding attribute from the tensor type.
+  return IREE::HAL::getEncodingTypeAttr({}, loc_.getContext());
+}
+
 llvm::Optional<SmallVector<Value, 4>> TensorRewriteAdaptor::getShapeDims() {
   return IREE::HAL::getShapeDims(loc_, oldValue_, rewriter_);
 }
@@ -189,22 +206,18 @@
 }
 
 Value TensorRewriteAdaptor::getByteLength() {
-  if (isBufferView()) {
-    return rewriter_.createOrFold<IREE::HAL::BufferViewByteLengthOp>(
-        loc_, getBufferView());
-  } else {
-    auto shapeDims = getShapeDims();
-    if (!shapeDims) return {};
-    return rewriter_.createOrFold<IREE::HAL::AllocatorComputeSizeOp>(
-        loc_, getAllocator(), *shapeDims, getElementType());
-  }
+  auto shapeDims = getShapeDims();
+  if (!shapeDims) return {};
+  return rewriter_.createOrFold<IREE::HAL::AllocatorComputeSizeOp>(
+      loc_, getAllocator(), *shapeDims, getElementType(), getEncodingType());
 }
 
 Value TensorRewriteAdaptor::computeOffset(ValueRange indices) {
   auto shapeDims = getShapeDims();
   if (!shapeDims) return {};
   return rewriter_.createOrFold<IREE::HAL::AllocatorComputeOffsetOp>(
-      loc_, getAllocator(), *shapeDims, getElementType(), indices);
+      loc_, getAllocator(), *shapeDims, getElementType(), getEncodingType(),
+      indices);
 }
 
 llvm::Optional<TensorRewriteAdaptor::Range> TensorRewriteAdaptor::computeRange(
@@ -212,7 +225,8 @@
   auto shapeDims = getShapeDims();
   if (!shapeDims) return llvm::None;
   auto range = rewriter_.create<IREE::HAL::AllocatorComputeRangeOp>(
-      loc_, getAllocator(), *shapeDims, getElementType(), indices, lengths);
+      loc_, getAllocator(), *shapeDims, getElementType(), getEncodingType(),
+      indices, lengths);
   return Range{range.offset(), range.length()};
 }
 
diff --git a/iree/compiler/Dialect/HAL/Utils/TypeUtils.h b/iree/compiler/Dialect/HAL/Utils/TypeUtils.h
index f91304a..d352c28 100644
--- a/iree/compiler/Dialect/HAL/Utils/TypeUtils.h
+++ b/iree/compiler/Dialect/HAL/Utils/TypeUtils.h
@@ -83,6 +83,10 @@
   int32_t getElementType();
   IntegerAttr getElementTypeAttr();
 
+  // Returns the encoding type of the tensor as an int32 enum value.
+  int32_t getEncodingType();
+  IntegerAttr getEncodingTypeAttr();
+
   // Returns the I32 shape dimensions of the tensor.
   llvm::Optional<SmallVector<Value, 4>> getShapeDims();
   llvm::Optional<SmallVector<Value, 4>> getShapeDims(
diff --git a/iree/compiler/Dialect/HAL/hal.imports.mlir b/iree/compiler/Dialect/HAL/hal.imports.mlir
index c51db3f..b30ff0a 100644
--- a/iree/compiler/Dialect/HAL/hal.imports.mlir
+++ b/iree/compiler/Dialect/HAL/hal.imports.mlir
@@ -78,6 +78,7 @@
 vm.import @buffer_view.create(
   %buffer : !vm.ref<!hal.buffer>,
   %element_type : i32,
+  %encoding_type : i32,
   %shape : i32 ...
 ) -> !vm.ref<!hal.buffer_view>
 attributes {nosideeffects}
@@ -100,6 +101,12 @@
 ) -> i32
 attributes {nosideeffects}
 
+// Returns the encoding type of the buffer view.
+vm.import @buffer_view.encoding_type(
+  %buffer_view : !vm.ref<!hal.buffer_view>,
+) -> i32
+attributes {nosideeffects}
+
 // Returns the rank of the buffer view.
 vm.import @buffer_view.rank(
   %buffer_view : !vm.ref<!hal.buffer_view>,
diff --git a/iree/hal/buffer_view.c b/iree/hal/buffer_view.c
index af464d2..7539dec 100644
--- a/iree/hal/buffer_view.c
+++ b/iree/hal/buffer_view.c
@@ -19,6 +19,7 @@
   iree_atomic_ref_count_t ref_count;
   iree_hal_buffer_t* buffer;
   iree_hal_element_type_t element_type;
+  iree_hal_encoding_type_t encoding_type;
   iree_device_size_t byte_length;
   iree_host_size_t shape_rank;
   iree_hal_dim_t shape[];
@@ -27,6 +28,7 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_create(
     iree_hal_buffer_t* buffer, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type,
     iree_hal_buffer_view_t** out_buffer_view) {
   IREE_ASSERT_ARGUMENT(buffer);
   IREE_ASSERT_ARGUMENT(out_buffer_view);
@@ -54,6 +56,7 @@
     buffer_view->buffer = buffer;
     iree_hal_buffer_retain(buffer_view->buffer);
     buffer_view->element_type = element_type;
+    buffer_view->encoding_type = encoding_type;
     buffer_view->byte_length =
         iree_hal_element_byte_count(buffer_view->element_type);
     buffer_view->shape_rank = shape_rank;
@@ -94,7 +97,8 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_allocate_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_buffer_usage_t allowed_usage,
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_buffer_usage_t allowed_usage,
     iree_hal_buffer_view_t** out_buffer_view) {
   IREE_ASSERT_ARGUMENT(allocator);
   IREE_ASSERT_ARGUMENT(out_buffer_view);
@@ -102,7 +106,7 @@
 
   iree_device_size_t allocation_size = 0;
   iree_status_t status = iree_hal_buffer_compute_view_size(
-      shape, shape_rank, element_type, &allocation_size);
+      shape, shape_rank, element_type, encoding_type, &allocation_size);
 
   iree_hal_buffer_t* buffer = NULL;
   if (iree_status_is_ok(status)) {
@@ -111,8 +115,9 @@
   }
 
   if (iree_status_is_ok(status)) {
-    status = iree_hal_buffer_view_create(buffer, shape, shape_rank,
-                                         element_type, out_buffer_view);
+    status =
+        iree_hal_buffer_view_create(buffer, shape, shape_rank, element_type,
+                                    encoding_type, out_buffer_view);
   }
 
   iree_hal_buffer_release(buffer);
@@ -123,8 +128,9 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_clone_heap_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_buffer_usage_t allowed_usage,
-    iree_const_byte_span_t data, iree_hal_buffer_view_t** out_buffer_view) {
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_buffer_usage_t allowed_usage, iree_const_byte_span_t data,
+    iree_hal_buffer_view_t** out_buffer_view) {
   IREE_ASSERT_ARGUMENT(allocator);
   IREE_ASSERT_ARGUMENT(out_buffer_view);
   IREE_TRACE_ZONE_BEGIN(z0);
@@ -132,9 +138,9 @@
   // Allocate the buffer.
   iree_hal_buffer_view_t* buffer_view = NULL;
   IREE_RETURN_AND_END_ZONE_IF_ERROR(
-      z0, iree_hal_buffer_view_allocate_buffer(allocator, shape, shape_rank,
-                                               element_type, memory_type,
-                                               allowed_usage, &buffer_view));
+      z0, iree_hal_buffer_view_allocate_buffer(
+              allocator, shape, shape_rank, element_type, encoding_type,
+              memory_type, allowed_usage, &buffer_view));
 
   // Copy all of the data into it in the worst way possible.
   // If you find yourself coming here from profiling:
@@ -156,7 +162,8 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_wrap_heap_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_memory_access_t allowed_access,
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_memory_access_t allowed_access,
     iree_hal_buffer_usage_t allowed_usage, iree_byte_span_t data,
     iree_allocator_t data_allocator, iree_hal_buffer_view_t** out_buffer_view) {
   IREE_ASSERT_ARGUMENT(allocator);
@@ -170,8 +177,9 @@
       data_allocator, &buffer);
 
   if (iree_status_is_ok(status)) {
-    status = iree_hal_buffer_view_create(buffer, shape, shape_rank,
-                                         element_type, out_buffer_view);
+    status =
+        iree_hal_buffer_view_create(buffer, shape, shape_rank, element_type,
+                                    encoding_type, out_buffer_view);
   }
 
   iree_hal_buffer_release(buffer);
@@ -182,7 +190,8 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_wrap_or_clone_heap_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_memory_access_t allowed_access,
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_memory_access_t allowed_access,
     iree_hal_buffer_usage_t allowed_usage, iree_byte_span_t data,
     iree_allocator_t data_allocator, iree_hal_buffer_view_t** out_buffer_view) {
   IREE_ASSERT_ARGUMENT(allocator);
@@ -200,12 +209,12 @@
       compatibility, IREE_HAL_BUFFER_COMPATIBILITY_IMPORTABLE);
   if (wrap_allowed) {
     return iree_hal_buffer_view_wrap_heap_buffer(
-        allocator, shape, shape_rank, element_type, memory_type, allowed_access,
-        allowed_usage, data, data_allocator, out_buffer_view);
+        allocator, shape, shape_rank, element_type, encoding_type, memory_type,
+        allowed_access, allowed_usage, data, data_allocator, out_buffer_view);
   } else {
     return iree_hal_buffer_view_clone_heap_buffer(
-        allocator, shape, shape_rank, element_type, memory_type, allowed_usage,
-        iree_make_const_byte_span(data.data, data.data_length),
+        allocator, shape, shape_rank, element_type, encoding_type, memory_type,
+        allowed_usage, iree_make_const_byte_span(data.data, data.data_length),
         out_buffer_view);
   }
 }
@@ -316,6 +325,12 @@
   return iree_hal_element_byte_count(buffer_view->element_type);
 }
 
+IREE_API_EXPORT iree_hal_encoding_type_t
+iree_hal_buffer_view_encoding_type(const iree_hal_buffer_view_t* buffer_view) {
+  IREE_ASSERT_ARGUMENT(buffer_view);
+  return buffer_view->encoding_type;
+}
+
 IREE_API_EXPORT iree_device_size_t
 iree_hal_buffer_view_byte_length(const iree_hal_buffer_view_t* buffer_view) {
   IREE_ASSERT_ARGUMENT(buffer_view);
@@ -328,7 +343,7 @@
   IREE_ASSERT_ARGUMENT(buffer_view);
   return iree_hal_buffer_compute_view_offset(
       buffer_view->shape, buffer_view->shape_rank, buffer_view->element_type,
-      indices, indices_count, out_offset);
+      buffer_view->encoding_type, indices, indices_count, out_offset);
 }
 
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_compute_range(
@@ -339,33 +354,51 @@
   IREE_ASSERT_ARGUMENT(buffer_view);
   return iree_hal_buffer_compute_view_range(
       buffer_view->shape, buffer_view->shape_rank, buffer_view->element_type,
-      start_indices, indices_count, lengths, lengths_count, out_start_offset,
-      out_length);
+      buffer_view->encoding_type, start_indices, indices_count, lengths,
+      lengths_count, out_start_offset, out_length);
 }
 
 IREE_API_EXPORT iree_status_t iree_hal_buffer_compute_view_size(
     const iree_hal_dim_t* shape, iree_host_size_t shape_rank,
     iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type,
     iree_device_size_t* out_allocation_size) {
   IREE_ASSERT_ARGUMENT(shape);
   IREE_ASSERT_ARGUMENT(out_allocation_size);
   *out_allocation_size = 0;
-  iree_device_size_t byte_length = iree_hal_element_byte_count(element_type);
-  for (iree_host_size_t i = 0; i < shape_rank; ++i) {
-    byte_length *= shape[i];
+
+  iree_device_size_t byte_length = 0;
+
+  switch (encoding_type) {
+    case IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR:
+      byte_length = iree_hal_element_byte_count(element_type);
+      for (iree_host_size_t i = 0; i < shape_rank; ++i) {
+        byte_length *= shape[i];
+      }
+      break;
+    default:
+      return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
+                              "unimplemented encoding type size calculation");
   }
+
   *out_allocation_size = byte_length;
   return iree_ok_status();
 }
 
 IREE_API_EXPORT iree_status_t iree_hal_buffer_compute_view_offset(
     const iree_hal_dim_t* shape, iree_host_size_t shape_rank,
-    iree_hal_element_type_t element_type, const iree_hal_dim_t* indices,
+    iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type, const iree_hal_dim_t* indices,
     iree_host_size_t indices_count, iree_device_size_t* out_offset) {
   IREE_ASSERT_ARGUMENT(shape);
   IREE_ASSERT_ARGUMENT(indices);
   IREE_ASSERT_ARGUMENT(out_offset);
   *out_offset = 0;
+  if (encoding_type != IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR) {
+    return iree_make_status(
+        IREE_STATUS_INVALID_ARGUMENT,
+        "only dense encodings support view range computation");
+  }
   if (IREE_UNLIKELY(shape_rank != indices_count)) {
     return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                             "shape rank/indices mismatch: %zu != %zu",
@@ -393,7 +426,8 @@
 
 IREE_API_EXPORT iree_status_t iree_hal_buffer_compute_view_range(
     const iree_hal_dim_t* shape, iree_host_size_t shape_rank,
-    iree_hal_element_type_t element_type, const iree_hal_dim_t* start_indices,
+    iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type, const iree_hal_dim_t* start_indices,
     iree_host_size_t indices_count, const iree_hal_dim_t* lengths,
     iree_host_size_t lengths_count, iree_device_size_t* out_start_offset,
     iree_device_size_t* out_length) {
@@ -404,6 +438,11 @@
   IREE_ASSERT_ARGUMENT(out_length);
   *out_start_offset = 0;
   *out_length = 0;
+  if (encoding_type != IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR) {
+    return iree_make_status(
+        IREE_STATUS_INVALID_ARGUMENT,
+        "only dense encodings support view range computation");
+  }
   if (IREE_UNLIKELY(indices_count != lengths_count)) {
     return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                             "indices/lengths mismatch: %zu != %zu",
@@ -426,11 +465,11 @@
 
   iree_device_size_t start_byte_offset = 0;
   IREE_RETURN_IF_ERROR(iree_hal_buffer_compute_view_offset(
-      shape, shape_rank, element_type, start_indices, indices_count,
-      &start_byte_offset));
+      shape, shape_rank, element_type, encoding_type, start_indices,
+      indices_count, &start_byte_offset));
   iree_device_size_t end_byte_offset = 0;
   IREE_RETURN_IF_ERROR(iree_hal_buffer_compute_view_offset(
-      shape, shape_rank, element_type, end_indices, shape_rank,
+      shape, shape_rank, element_type, encoding_type, end_indices, shape_rank,
       &end_byte_offset));
 
   // Non-contiguous regions not yet implemented. Will be easier to detect when
@@ -482,10 +521,6 @@
                                        IREE_STRING_VIEW_NPOS);
   }
 
-  // f32, i32, etc
-  iree_hal_element_type_t element_type = IREE_HAL_ELEMENT_TYPE_NONE;
-  IREE_RETURN_IF_ERROR(iree_hal_parse_element_type(type_str, &element_type));
-
   // AxBxC...
   iree_host_size_t shape_rank = 0;
   iree_status_t shape_result =
@@ -504,10 +539,18 @@
   IREE_RETURN_IF_ERROR(
       iree_hal_parse_shape(shape_str, shape_rank, shape, &shape_rank));
 
+  // f32, i32, etc
+  iree_hal_element_type_t element_type = IREE_HAL_ELEMENT_TYPE_NONE;
+  IREE_RETURN_IF_ERROR(iree_hal_parse_element_type(type_str, &element_type));
+
+  // TODO(benvanik): allow specifying the encoding.
+  iree_hal_encoding_type_t encoding_type =
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
+
   // Allocate the buffer we will parse into from the provided allocator.
   iree_device_size_t buffer_length = 0;
   IREE_RETURN_IF_ERROR(iree_hal_buffer_compute_view_size(
-      shape, shape_rank, element_type, &buffer_length));
+      shape, shape_rank, element_type, encoding_type, &buffer_length));
   iree_hal_buffer_t* buffer = NULL;
   IREE_RETURN_IF_ERROR(iree_hal_allocator_allocate_buffer(
       buffer_allocator,
@@ -535,7 +578,7 @@
 
   // Wrap and pass ownership of the buffer to the buffer view.
   status = iree_hal_buffer_view_create(buffer, shape, shape_rank, element_type,
-                                       out_buffer_view);
+                                       encoding_type, out_buffer_view);
   iree_hal_buffer_release(buffer);
   return status;
 }
@@ -612,6 +655,8 @@
     return status;
   }
 
+  // TODO(benvanik): allow printing the encoding.
+
   // Separator: <meta>=<value>
   APPEND_CHAR('=');
 
diff --git a/iree/hal/buffer_view.h b/iree/hal/buffer_view.h
index a4595e6..310a487 100644
--- a/iree/hal/buffer_view.h
+++ b/iree/hal/buffer_view.h
@@ -77,6 +77,23 @@
 typedef uint32_t iree_hal_element_type_t;
 // clang-format on
 
+// Defines the encoding type of a buffer when known.
+enum iree_hal_encoding_types_t {
+  // Encoding is unknown or unspecified. Generic interpretation of the buffer
+  // contents is not possible.
+  IREE_HAL_ENCODING_TYPE_OPAQUE = 0,
+  // Encoding is a densely-packed numpy/C-style row-major format.
+  // All elements are contiguous in memory.
+  IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR = 1,
+  // TODO(#6762): sparse encodings we care about (_SPARSE_CSR)
+  // We will likely want to make this a bitfield like the element type is that
+  // we can more easily distinguish between encoding types that we can use for
+  // certain operations; for example, size calculations on a DENSE_ROW_MAJOR
+  // and DENSE_COLUMN_MAJOR would be easier to perform if we had a bit to test
+  // for whether it's dense.
+};
+typedef uint32_t iree_hal_encoding_type_t;
+
 // A dimension within a shape.
 typedef int32_t iree_hal_dim_t;
 
@@ -88,18 +105,23 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_compute_view_size(
     const iree_hal_dim_t* shape, iree_host_size_t shape_rank,
     iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type,
     iree_device_size_t* out_allocation_size);
 
 // Calculates a byte offset into a buffer at the given indices.
+// Only works with densely-packed representations.
 IREE_API_EXPORT iree_status_t iree_hal_buffer_compute_view_offset(
     const iree_hal_dim_t* shape, iree_host_size_t shape_rank,
-    iree_hal_element_type_t element_type, const iree_hal_dim_t* indices,
+    iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type, const iree_hal_dim_t* indices,
     size_t indices_count, iree_device_size_t* out_offset);
 
 // Calculates a byte range into a buffer of the given contiguous range.
+// Only works with densely-packed representations.
 IREE_API_EXPORT iree_status_t iree_hal_buffer_compute_view_range(
     const iree_hal_dim_t* shape, iree_host_size_t shape_rank,
-    iree_hal_element_type_t element_type, const iree_hal_dim_t* start_indices,
+    iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type, const iree_hal_dim_t* start_indices,
     iree_host_size_t indices_count, const iree_hal_dim_t* lengths,
     iree_host_size_t lengths_count, iree_device_size_t* out_start_offset,
     iree_device_size_t* out_length);
@@ -121,6 +143,7 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_create(
     iree_hal_buffer_t* buffer, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
+    iree_hal_encoding_type_t encoding_type,
     iree_hal_buffer_view_t** out_buffer_view);
 
 // Allocates a buffer from |allocator| and wraps it in a buffer view.
@@ -131,7 +154,8 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_allocate_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_buffer_usage_t allowed_usage,
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_buffer_usage_t allowed_usage,
     iree_hal_buffer_view_t** out_buffer_view);
 
 // Clones a host buffer using |allocator| and wraps it in a buffer view.
@@ -146,8 +170,9 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_clone_heap_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_buffer_usage_t allowed_usage,
-    iree_const_byte_span_t data, iree_hal_buffer_view_t** out_buffer_view);
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_buffer_usage_t allowed_usage, iree_const_byte_span_t data,
+    iree_hal_buffer_view_t** out_buffer_view);
 
 // Imports a host buffer using |allocator| and wraps it in a buffer view.
 // This is equivalent to:
@@ -160,7 +185,8 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_wrap_heap_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_memory_access_t allowed_access,
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_memory_access_t allowed_access,
     iree_hal_buffer_usage_t allowed_usage, iree_byte_span_t data,
     iree_allocator_t data_allocator, iree_hal_buffer_view_t** out_buffer_view);
 
@@ -178,7 +204,8 @@
 IREE_API_EXPORT iree_status_t iree_hal_buffer_view_wrap_or_clone_heap_buffer(
     iree_hal_allocator_t* allocator, const iree_hal_dim_t* shape,
     iree_host_size_t shape_rank, iree_hal_element_type_t element_type,
-    iree_hal_memory_type_t memory_type, iree_hal_memory_access_t allowed_access,
+    iree_hal_encoding_type_t encoding_type, iree_hal_memory_type_t memory_type,
+    iree_hal_memory_access_t allowed_access,
     iree_hal_buffer_usage_t allowed_usage, iree_byte_span_t data,
     iree_allocator_t data_allocator, iree_hal_buffer_view_t** out_buffer_view);
 
@@ -243,6 +270,10 @@
 IREE_API_EXPORT iree_host_size_t
 iree_hal_buffer_view_element_size(const iree_hal_buffer_view_t* buffer_view);
 
+// Returns the encoding type of the buffer.
+IREE_API_EXPORT iree_hal_encoding_type_t
+iree_hal_buffer_view_encoding_type(const iree_hal_buffer_view_t* buffer_view);
+
 // Returns the total size of the specified view in bytes.
 // Note that not all buffers are contiguous or densely packed.
 IREE_API_EXPORT iree_device_size_t
diff --git a/iree/hal/string_util_test.cc b/iree/hal/string_util_test.cc
index 968f66e..c661520 100644
--- a/iree/hal/string_util_test.cc
+++ b/iree/hal/string_util_test.cc
@@ -427,9 +427,12 @@
   static StatusOr<BufferView> Create(Buffer buffer,
                                      iree::span<const iree_hal_dim_t> shape,
                                      iree_hal_element_type_t element_type) {
+    iree_hal_encoding_type_t encoding_type =
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
     BufferView buffer_view;
-    iree_status_t status = iree_hal_buffer_view_create(
-        buffer, shape.data(), shape.size(), element_type, &buffer_view);
+    iree_status_t status =
+        iree_hal_buffer_view_create(buffer, shape.data(), shape.size(),
+                                    element_type, encoding_type, &buffer_view);
     IREE_RETURN_IF_ERROR(std::move(status));
     return std::move(buffer_view);
   }
diff --git a/iree/modules/check/check_test.cc b/iree/modules/check/check_test.cc
index caafb2c..dbf03c0 100644
--- a/iree/modules/check/check_test.cc
+++ b/iree/modules/check/check_test.cc
@@ -91,7 +91,7 @@
         buffer.get(), 0, contents.data(), contents.size() * sizeof(int32_t)));
     IREE_ASSERT_OK(iree_hal_buffer_view_create(
         buffer.get(), shape.data(), shape.size(), IREE_HAL_ELEMENT_TYPE_SINT_32,
-        &*out_buffer_view));
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, &*out_buffer_view));
   }
 
   void CreateFloat16BufferView(iree::span<const uint16_t> contents,
@@ -114,7 +114,8 @@
         buffer.get(), 0, contents.data(), contents.size() * sizeof(uint16_t)));
     IREE_ASSERT_OK(iree_hal_buffer_view_create(
         buffer.get(), shape.data(), shape.size(),
-        IREE_HAL_ELEMENT_TYPE_FLOAT_16, &*out_buffer_view));
+        IREE_HAL_ELEMENT_TYPE_FLOAT_16, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
+        &*out_buffer_view));
   }
 
   void CreateFloat32BufferView(iree::span<const float> contents,
@@ -136,7 +137,8 @@
                                               contents.size() * sizeof(float)));
     IREE_ASSERT_OK(iree_hal_buffer_view_create(
         buffer.get(), shape.data(), shape.size(),
-        IREE_HAL_ELEMENT_TYPE_FLOAT_32, &*out_buffer_view));
+        IREE_HAL_ELEMENT_TYPE_FLOAT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
+        &*out_buffer_view));
   }
 
   void CreateFloat64BufferView(iree::span<const double> contents,
@@ -158,7 +160,8 @@
         buffer.get(), 0, contents.data(), contents.size() * sizeof(double)));
     IREE_ASSERT_OK(iree_hal_buffer_view_create(
         buffer.get(), shape.data(), shape.size(),
-        IREE_HAL_ELEMENT_TYPE_FLOAT_64, &*out_buffer_view));
+        IREE_HAL_ELEMENT_TYPE_FLOAT_64, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
+        &*out_buffer_view));
   }
 
   iree_status_t Invoke(const char* function_name) {
diff --git a/iree/modules/hal/exports.inl b/iree/modules/hal/exports.inl
index 8d8adc3..cd49cb4 100644
--- a/iree/modules/hal/exports.inl
+++ b/iree/modules/hal/exports.inl
@@ -34,9 +34,10 @@
 
 EXPORT_FN("buffer_view.buffer", iree_hal_module_buffer_view_buffer, r, r)
 EXPORT_FN("buffer_view.byte_length", iree_hal_module_buffer_view_byte_length, r, i)
-EXPORT_FN("buffer_view.create", iree_hal_module_buffer_view_create, riCiD, r)
+EXPORT_FN("buffer_view.create", iree_hal_module_buffer_view_create, riiCiD, r)
 EXPORT_FN("buffer_view.dim", iree_hal_module_buffer_view_dim, ri, i)
 EXPORT_FN("buffer_view.element_type", iree_hal_module_buffer_view_element_type, r, i)
+EXPORT_FN("buffer_view.encoding_type", iree_hal_module_buffer_view_encoding_type, r, i)
 EXPORT_FN("buffer_view.rank", iree_hal_module_buffer_view_rank, r, i)
 EXPORT_FN("buffer_view.trace", iree_hal_module_buffer_view_trace, rCrD, v)
 
diff --git a/iree/modules/hal/module.c b/iree/modules/hal/module.c
index 14924d9..6a74c98 100644
--- a/iree/modules/hal/module.c
+++ b/iree/modules/hal/module.c
@@ -400,18 +400,20 @@
 
 IREE_VM_ABI_EXPORT(iree_hal_module_buffer_view_create,  //
                    iree_hal_module_state_t,             //
-                   riCiD, r) {
+                   riiCiD, r) {
   iree_hal_buffer_t* source_buffer = NULL;
   IREE_RETURN_IF_ERROR(iree_hal_buffer_check_deref(args->r0, &source_buffer));
   iree_hal_element_type_t element_type = (iree_hal_element_type_t)args->i1;
+  iree_hal_encoding_type_t encoding_type = (iree_hal_encoding_type_t)args->i2;
   iree_host_size_t shape_rank = 0;
   iree_hal_dim_t* shape_dims = NULL;
-  IREE_VM_ABI_VLA_STACK_CAST(args, a2_count, a2, iree_hal_dim_t, 128,
+  IREE_VM_ABI_VLA_STACK_CAST(args, a3_count, a3, iree_hal_dim_t, 128,
                              &shape_rank, &shape_dims);
 
   iree_hal_buffer_view_t* buffer_view = NULL;
-  IREE_RETURN_IF_ERROR(iree_hal_buffer_view_create(
-      source_buffer, shape_dims, shape_rank, element_type, &buffer_view));
+  IREE_RETURN_IF_ERROR(
+      iree_hal_buffer_view_create(source_buffer, shape_dims, shape_rank,
+                                  element_type, encoding_type, &buffer_view));
   rets->r0 = iree_hal_buffer_view_move_ref(buffer_view);
   return iree_ok_status();
 }
@@ -447,6 +449,16 @@
   return iree_ok_status();
 }
 
+IREE_VM_ABI_EXPORT(iree_hal_module_buffer_view_encoding_type,  //
+                   iree_hal_module_state_t,                    //
+                   r, i) {
+  iree_hal_buffer_view_t* buffer_view = NULL;
+  IREE_RETURN_IF_ERROR(
+      iree_hal_buffer_view_check_deref(args->r0, &buffer_view));
+  rets->i0 = (uint32_t)iree_hal_buffer_view_encoding_type(buffer_view);
+  return iree_ok_status();
+}
+
 IREE_VM_ABI_EXPORT(iree_hal_module_buffer_view_rank,  //
                    iree_hal_module_state_t,           //
                    r, i) {
diff --git a/iree/runtime/demo/hello_world_explained.c b/iree/runtime/demo/hello_world_explained.c
index fb4f7e2..cbdfb09 100644
--- a/iree/runtime/demo/hello_world_explained.c
+++ b/iree/runtime/demo/hello_world_explained.c
@@ -196,6 +196,8 @@
           arg0_shape, IREE_ARRAYSIZE(arg0_shape),
           // Element type:
           IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+          // Encoding type:
+          IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
           // Where to allocate (host or device):
           IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
           // What access to allow to this memory (this is .rodata so READ only):
@@ -227,6 +229,7 @@
       status = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
           device_allocator, arg1_shape, IREE_ARRAYSIZE(arg1_shape),
           IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+          IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
           IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
           IREE_HAL_MEMORY_ACCESS_READ, IREE_HAL_BUFFER_USAGE_ALL,
           iree_make_byte_span((void*)arg1_data, sizeof(arg1_data)),
diff --git a/iree/runtime/demo/hello_world_terse.c b/iree/runtime/demo/hello_world_terse.c
index 2a71bb8..3a10d32 100644
--- a/iree/runtime/demo/hello_world_terse.c
+++ b/iree/runtime/demo/hello_world_terse.c
@@ -82,6 +82,7 @@
   IREE_CHECK_OK(iree_hal_buffer_view_wrap_or_clone_heap_buffer(
       iree_runtime_session_device_allocator(session), arg0_shape,
       IREE_ARRAYSIZE(arg0_shape), IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
       IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
       IREE_HAL_MEMORY_ACCESS_READ, IREE_HAL_BUFFER_USAGE_ALL,
       iree_make_byte_span((void*)arg0_data, sizeof(arg0_data)),
@@ -100,6 +101,7 @@
   IREE_CHECK_OK(iree_hal_buffer_view_wrap_or_clone_heap_buffer(
       iree_runtime_session_device_allocator(session), arg1_shape,
       IREE_ARRAYSIZE(arg1_shape), IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
       IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
       IREE_HAL_MEMORY_ACCESS_READ, IREE_HAL_BUFFER_USAGE_ALL,
       iree_make_byte_span((void*)arg1_data, sizeof(arg1_data)),
diff --git a/iree/samples/custom_modules/custom_modules_test.cc b/iree/samples/custom_modules/custom_modules_test.cc
index 1dc2052..10f9b15 100644
--- a/iree/samples/custom_modules/custom_modules_test.cc
+++ b/iree/samples/custom_modules/custom_modules_test.cc
@@ -135,7 +135,7 @@
   iree_hal_buffer_view_t* buffer_view = nullptr;
   IREE_ASSERT_OK(iree_hal_buffer_view_wrap_or_clone_heap_buffer(
       hal_allocator_, kShape, IREE_ARRAYSIZE(kShape),
-      IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+      IREE_HAL_ELEMENT_TYPE_FLOAT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
       IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
       IREE_HAL_MEMORY_ACCESS_ALL, IREE_HAL_BUFFER_USAGE_ALL,
       iree_make_byte_span((void*)kBufferContents, sizeof(kBufferContents)),
@@ -179,7 +179,7 @@
   iree_hal_buffer_view_t* buffer_view = nullptr;
   IREE_ASSERT_OK(iree_hal_buffer_view_wrap_or_clone_heap_buffer(
       hal_allocator_, kShape, IREE_ARRAYSIZE(kShape),
-      IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+      IREE_HAL_ELEMENT_TYPE_FLOAT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
       IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
       IREE_HAL_MEMORY_ACCESS_ALL, IREE_HAL_BUFFER_USAGE_ALL,
       iree_make_byte_span((void*)kBufferContents, sizeof(kBufferContents)),
diff --git a/iree/samples/custom_modules/dialect/test/conversion.mlir b/iree/samples/custom_modules/dialect/test/conversion.mlir
index d7bf428..34e5a32 100644
--- a/iree/samples/custom_modules/dialect/test/conversion.mlir
+++ b/iree/samples/custom_modules/dialect/test/conversion.mlir
@@ -13,10 +13,11 @@
 // CHECK-LABEL: @tensorToMessage
 func @tensorToMessage(%tensor : tensor<2x4xf32>) {
   //  CHECK-DAG: [[TYPE:%.+]] = vm.const.i32 50331680 : i32
+  //  CHECK-DAG: [[ENCODING:%.+]] = vm.const.i32 1 : i32
   //  CHECK-DAG: [[DIM0:%.+]] = vm.const.i32 2 : i32
   //  CHECK-DAG: [[DIM1:%.+]] = vm.const.i32 4 : i32
   // CHECK-NEXT: [[VIEW:%.+]] = vm.call.variadic @hal.buffer_view.create(
-  // CHECK-SAME:     %arg0, [[TYPE]], [
+  // CHECK-SAME:     %arg0, [[TYPE]], [[ENCODING]], [
   // CHECK-SAME:       [[DIM0]], [[DIM1]]
   // CHECK-SAME:     ])
   // CHECK-NEXT: [[MSG:%.+]] = vm.call @custom.buffer_to_message([[VIEW]]) : (!vm.ref<!hal.buffer_view>) -> !vm.ref<!custom.message>
@@ -32,10 +33,9 @@
 // CHECK-LABEL: @dynamicTensorToMessage
 func @dynamicTensorToMessage(%arg0 : tensor<?x?xf32>, %arg1 : index, %arg2 : index) {
   //  CHECK-DAG: [[TYPE:%.+]] = vm.const.i32 50331680 : i32
+  //  CHECK-DAG: [[ENCODING:%.+]] = vm.const.i32 1 : i32
   // CHECK-NEXT: [[VIEW:%.+]] = vm.call.variadic @hal.buffer_view.create(
-  // CHECK-SAME:     %arg0, [[TYPE]], [
-  // CHECK-SAME:       %arg1, %arg2
-  // CHECK-SAME:     ])
+  // CHECK-SAME:     %arg0, [[TYPE]], [[ENCODING]], [%arg1, %arg2])
   // CHECK-NEXT: [[MSG:%.+]] = vm.call @custom.buffer_to_message([[VIEW]]) : (!vm.ref<!hal.buffer_view>) -> !vm.ref<!custom.message>
   %shape = shapex.make_ranked_shape %arg1, %arg2 : (index, index) -> !shapex.ranked_shape<[?, ?]>
   %shaped_tensor = shapex.tie_shape %arg0, %shape : tensor<?x?xf32>, !shapex.ranked_shape<[?, ?]>
@@ -50,9 +50,10 @@
 
 // CHECK-LABEL: @dynamicTensorToMessage2
 func @dynamicTensorToMessage2(%arg0 : tensor<?x?xf32>, %arg1: !shapex.ranked_shape<[?, ?]> {iree.reflection = {}}) {
-  // CHECK-DAG: [[TYPE:%.+]] = vm.const.i32 50331680 : i32
-  // CHECK-NEXT: [[VIEW:%.+]] = vm.call.variadic @hal.buffer_view.create(%arg0, [[TYPE]],
-  // CHECK-SAME: [%arg1, %arg2])
+  //  CHECK-DAG: [[TYPE:%.+]] = vm.const.i32 50331680 : i32
+  //  CHECK-DAG: [[ENCODING:%.+]] = vm.const.i32 1 : i32
+  // CHECK-NEXT: [[VIEW:%.+]] = vm.call.variadic @hal.buffer_view.create(
+  // CHECK-SAME:     %arg0, [[TYPE]], [[ENCODING]], [%arg1, %arg2])
   // CHECK-NEXT: [[MSG:%.+]] = vm.call @custom.buffer_to_message([[VIEW]]) : (!vm.ref<!hal.buffer_view>) -> !vm.ref<!custom.message>
   %shaped_tensor = shapex.tie_shape %arg0, %arg1 : tensor<?x?xf32>, !shapex.ranked_shape<[?, ?]>
   %0 = "custom.tensor_to_message"(%shaped_tensor) : (tensor<?x?xf32>) -> !custom.message
diff --git a/iree/samples/dynamic_shapes/main.c b/iree/samples/dynamic_shapes/main.c
index 1e1838d..f82737c 100644
--- a/iree/samples/dynamic_shapes/main.c
+++ b/iree/samples/dynamic_shapes/main.c
@@ -24,6 +24,7 @@
     status = iree_hal_buffer_view_clone_heap_buffer(
         iree_runtime_session_device_allocator(session), arg0_shape,
         IREE_ARRAYSIZE(arg0_shape), IREE_HAL_ELEMENT_TYPE_SINT_32,
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
         IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
         IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_const_byte_span((void*)values, sizeof(int) * values_length),
@@ -75,6 +76,7 @@
     status = iree_hal_buffer_view_clone_heap_buffer(
         iree_runtime_session_device_allocator(session), arg0_shape,
         IREE_ARRAYSIZE(arg0_shape), IREE_HAL_ELEMENT_TYPE_SINT_32,
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
         IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
         IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_const_byte_span((void*)values, sizeof(int) * values_length),
@@ -114,6 +116,7 @@
     status = iree_hal_buffer_view_clone_heap_buffer(
         iree_runtime_session_device_allocator(session), arg0_shape,
         IREE_ARRAYSIZE(arg0_shape), IREE_HAL_ELEMENT_TYPE_SINT_32,
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
         IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
         IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_const_byte_span((void*)values, sizeof(int) * values_length),
diff --git a/iree/samples/simple_embedding/simple_embedding.c b/iree/samples/simple_embedding/simple_embedding.c
index 2f5645d..c04a15e 100644
--- a/iree/samples/simple_embedding/simple_embedding.c
+++ b/iree/samples/simple_embedding/simple_embedding.c
@@ -92,10 +92,10 @@
   iree_hal_buffer_view_t* arg1_buffer_view = NULL;
   IREE_RETURN_IF_ERROR(iree_hal_buffer_view_create(
       arg0_buffer, shape, IREE_ARRAYSIZE(shape), IREE_HAL_ELEMENT_TYPE_FLOAT_32,
-      &arg0_buffer_view));
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, &arg0_buffer_view));
   IREE_RETURN_IF_ERROR(iree_hal_buffer_view_create(
       arg1_buffer, shape, IREE_ARRAYSIZE(shape), IREE_HAL_ELEMENT_TYPE_FLOAT_32,
-      &arg1_buffer_view));
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, &arg1_buffer_view));
   iree_hal_buffer_release(arg0_buffer);
   iree_hal_buffer_release(arg1_buffer);
 
diff --git a/iree/samples/static_library/static_library_demo.c b/iree/samples/static_library/static_library_demo.c
index 47b32c2..350edac 100644
--- a/iree/samples/static_library/static_library_demo.c
+++ b/iree/samples/static_library/static_library_demo.c
@@ -133,8 +133,8 @@
   if (iree_status_is_ok(status)) {
     iree_hal_buffer_view_clone_heap_buffer(
         iree_hal_device_allocator(device), shape, IREE_ARRAYSIZE(shape),
-        IREE_HAL_ELEMENT_TYPE_FLOAT_32, input_memory_type,
-        IREE_HAL_BUFFER_USAGE_ALL,
+        IREE_HAL_ELEMENT_TYPE_FLOAT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
+        input_memory_type, IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_const_byte_span((void*)kFloat4,
                                   sizeof(float) * kElementCount),
         &arg0_buffer_view);
@@ -142,8 +142,8 @@
   if (iree_status_is_ok(status)) {
     iree_hal_buffer_view_clone_heap_buffer(
         iree_hal_device_allocator(device), shape, IREE_ARRAYSIZE(shape),
-        IREE_HAL_ELEMENT_TYPE_FLOAT_32, input_memory_type,
-        IREE_HAL_BUFFER_USAGE_ALL,
+        IREE_HAL_ELEMENT_TYPE_FLOAT_32, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
+        input_memory_type, IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_const_byte_span((void*)kFloat2,
                                   sizeof(float) * kElementCount),
         &arg1_buffer_view);
diff --git a/iree/samples/variables_and_state/main.c b/iree/samples/variables_and_state/main.c
index c2fe84d..5abb8a0 100644
--- a/iree/samples/variables_and_state/main.c
+++ b/iree/samples/variables_and_state/main.c
@@ -56,6 +56,7 @@
     status = iree_hal_buffer_view_clone_heap_buffer(
         iree_runtime_session_device_allocator(session), arg0_shape,
         IREE_ARRAYSIZE(arg0_shape), IREE_HAL_ELEMENT_TYPE_SINT_32,
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
         IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
         IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_const_byte_span((void*)arg0_data, sizeof(arg0_data)), &arg0);
@@ -88,6 +89,7 @@
     status = iree_hal_buffer_view_clone_heap_buffer(
         iree_runtime_session_device_allocator(session), arg0_shape,
         IREE_ARRAYSIZE(arg0_shape), IREE_HAL_ELEMENT_TYPE_SINT_32,
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
         IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
         IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_const_byte_span((void*)arg0_data, sizeof(arg0_data)), &arg0);
diff --git a/iree/samples/vulkan/vulkan_inference_gui.cc b/iree/samples/vulkan/vulkan_inference_gui.cc
index c688a9f..f30b810 100644
--- a/iree/samples/vulkan/vulkan_inference_gui.cc
+++ b/iree/samples/vulkan/vulkan_inference_gui.cc
@@ -388,11 +388,13 @@
         IREE_CHECK_OK(iree_hal_buffer_view_create(
             input0_buffer,
             /*shape=*/&kElementCount, /*shape_rank=*/1,
-            IREE_HAL_ELEMENT_TYPE_FLOAT_32, &input0_buffer_view));
+            IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+            IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, &input0_buffer_view));
         IREE_CHECK_OK(iree_hal_buffer_view_create(
             input1_buffer,
             /*shape=*/&kElementCount, /*shape_rank=*/1,
-            IREE_HAL_ELEMENT_TYPE_FLOAT_32, &input1_buffer_view));
+            IREE_HAL_ELEMENT_TYPE_FLOAT_32,
+            IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR, &input1_buffer_view));
         iree_hal_buffer_release(input0_buffer);
         iree_hal_buffer_release(input1_buffer);
         // Marshal inputs through a VM variant list.
diff --git a/iree/tools/utils/image_util.c b/iree/tools/utils/image_util.c
index 6e9440c..60f7708 100644
--- a/iree/tools/utils/image_util.c
+++ b/iree/tools/utils/image_util.c
@@ -136,6 +136,7 @@
     // SINT_8 and UINT_8 perform direct buffer wrap.
     result = iree_hal_buffer_view_wrap_or_clone_heap_buffer(
         allocator, shape, shape_rank, element_type,
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
         IREE_HAL_MEMORY_TYPE_HOST_LOCAL | IREE_HAL_MEMORY_TYPE_DEVICE_VISIBLE,
         IREE_HAL_MEMORY_ACCESS_READ, IREE_HAL_BUFFER_USAGE_ALL,
         iree_make_byte_span((void*)pixel_data, element_byte * buffer_length),
@@ -186,8 +187,11 @@
     iree_hal_buffer_unmap_range(&mapped_memory);
   }
   if (iree_status_is_ok(result)) {
-    result = iree_hal_buffer_view_create(buffer, shape, shape_rank,
-                                         element_type, out_buffer_view);
+    iree_hal_encoding_type_t encoding_type =
+        IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
+    result =
+        iree_hal_buffer_view_create(buffer, shape, shape_rank, element_type,
+                                    encoding_type, out_buffer_view);
   }
   iree_hal_buffer_release(buffer);
   stbi_image_free(pixel_data);
diff --git a/iree/tools/utils/trace_replay.c b/iree/tools/utils/trace_replay.c
index faa8601..0830a13 100644
--- a/iree/tools/utils/trace_replay.c
+++ b/iree/tools/utils/trace_replay.c
@@ -417,6 +417,42 @@
   return iree_hal_parse_element_type(element_type_str, out_element_type);
 }
 
+// Parses an encoding type.
+//
+// ```yaml
+// encoding_type: 50331680
+// ```
+static iree_status_t iree_trace_replay_parse_hal_encoding_type(
+    iree_trace_replay_t* replay, yaml_document_t* document,
+    yaml_node_t* encoding_type_node,
+    iree_hal_encoding_type_t* out_encoding_type) {
+  *out_encoding_type = IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
+
+  iree_string_view_t encoding_type_str =
+      iree_yaml_node_as_string(encoding_type_node);
+  if (iree_string_view_is_empty(encoding_type_str)) {
+    return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+                            "(%zu): encoding type missing",
+                            encoding_type_node->start_mark.line);
+  }
+
+  // If the first character is a digit then interpret as a %d type.
+  if (isdigit(encoding_type_str.data[0])) {
+    static_assert(sizeof(*out_encoding_type) == sizeof(uint32_t), "4 bytes");
+    if (!iree_string_view_atoi_uint32(encoding_type_str, out_encoding_type)) {
+      return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
+                              "(%zu): invalid encoding type",
+                              encoding_type_node->start_mark.line);
+    }
+    return iree_ok_status();
+  }
+
+  // Parse as a canonical encoding type.
+  // TODO(#6762): implement iree_hal_parse_element_type.
+  return iree_make_status(IREE_STATUS_UNIMPLEMENTED,
+                          "iree_hal_parse_encoding_type not implemented");
+}
+
 // Parses a serialized !hal.buffer into |buffer|.
 //
 // ```yaml
@@ -469,14 +505,6 @@
 static iree_status_t iree_trace_replay_parse_hal_buffer_view(
     iree_trace_replay_t* replay, yaml_document_t* document,
     yaml_node_t* value_node, iree_vm_list_t* target_list) {
-  yaml_node_t* element_type_node = NULL;
-  IREE_RETURN_IF_ERROR(iree_yaml_mapping_find(
-      document, value_node, iree_make_cstring_view("element_type"),
-      &element_type_node));
-  iree_hal_element_type_t element_type = IREE_HAL_ELEMENT_TYPE_NONE;
-  IREE_RETURN_IF_ERROR(iree_trace_replay_parse_hal_element_type(
-      replay, document, element_type_node, &element_type));
-
   yaml_node_t* shape_node = NULL;
   IREE_RETURN_IF_ERROR(iree_yaml_mapping_try_find(
       document, value_node, iree_make_cstring_view("shape"), &shape_node));
@@ -485,6 +513,23 @@
   IREE_RETURN_IF_ERROR(iree_trace_replay_parse_hal_shape(
       replay, document, shape_node, IREE_ARRAYSIZE(shape), shape, &shape_rank));
 
+  yaml_node_t* element_type_node = NULL;
+  IREE_RETURN_IF_ERROR(iree_yaml_mapping_find(
+      document, value_node, iree_make_cstring_view("element_type"),
+      &element_type_node));
+  iree_hal_element_type_t element_type = IREE_HAL_ELEMENT_TYPE_NONE;
+  IREE_RETURN_IF_ERROR(iree_trace_replay_parse_hal_element_type(
+      replay, document, element_type_node, &element_type));
+
+  yaml_node_t* encoding_type_node = NULL;
+  IREE_RETURN_IF_ERROR(iree_yaml_mapping_try_find(
+      document, value_node, iree_make_cstring_view("encoding_type"),
+      &encoding_type_node));
+  iree_hal_encoding_type_t encoding_type =
+      IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR;
+  IREE_RETURN_IF_ERROR(iree_trace_replay_parse_hal_encoding_type(
+      replay, document, encoding_type_node, &encoding_type));
+
   yaml_node_t* contents_node = NULL;
   IREE_RETURN_IF_ERROR(iree_yaml_mapping_try_find(
       document, value_node, iree_make_cstring_view("contents"),
@@ -492,7 +537,8 @@
 
   iree_device_size_t allocation_size = 0;
   IREE_RETURN_IF_ERROR(iree_hal_buffer_compute_view_size(
-      shape, shape_rank, element_type, &allocation_size));
+      shape, shape_rank, element_type, IREE_HAL_ENCODING_TYPE_DENSE_ROW_MAJOR,
+      &allocation_size));
 
   iree_hal_buffer_t* buffer = NULL;
   IREE_RETURN_IF_ERROR(iree_hal_allocator_allocate_buffer(
@@ -508,7 +554,7 @@
 
   iree_hal_buffer_view_t* buffer_view = NULL;
   status = iree_hal_buffer_view_create(buffer, shape, shape_rank, element_type,
-                                       &buffer_view);
+                                       encoding_type, &buffer_view);
   iree_hal_buffer_release(buffer);
   IREE_RETURN_IF_ERROR(status);
 
diff --git a/iree/vm/shims.c b/iree/vm/shims.c
index 8d66693..bac9544 100644
--- a/iree/vm/shims.c
+++ b/iree/vm/shims.c
@@ -19,6 +19,7 @@
 IREE_VM_ABI_DEFINE_SHIM(ri, r);
 IREE_VM_ABI_DEFINE_SHIM(ri, v);
 IREE_VM_ABI_DEFINE_SHIM(riCiD, r);
+IREE_VM_ABI_DEFINE_SHIM(riiCiD, r);
 IREE_VM_ABI_DEFINE_SHIM(riCiiiD, r);
 IREE_VM_ABI_DEFINE_SHIM(riCrD, r);
 IREE_VM_ABI_DEFINE_SHIM(rii, i);
diff --git a/iree/vm/shims.h b/iree/vm/shims.h
index 686ced9..d13cf8c 100644
--- a/iree/vm/shims.h
+++ b/iree/vm/shims.h
@@ -296,6 +296,14 @@
   iree_vm_abi_i_t a2[0];
 });
 
+IREE_VM_ABI_VLA_STRUCT(riiCiD, a3_count, a3, {
+  iree_vm_ref_t r0;
+  int32_t i1;
+  int32_t i2;
+  iree_vm_size_t a3_count;
+  iree_vm_abi_i_t a3[0];
+});
+
 IREE_VM_ABI_VLA_STRUCT(riCrD, a2_count, a2, {
   iree_vm_ref_t r0;
   int32_t i1;
@@ -388,6 +396,7 @@
 IREE_VM_ABI_DECLARE_SHIM(ri, r);
 IREE_VM_ABI_DECLARE_SHIM(ri, v);
 IREE_VM_ABI_DECLARE_SHIM(riCiD, r);
+IREE_VM_ABI_DECLARE_SHIM(riiCiD, r);
 IREE_VM_ABI_DECLARE_SHIM(riCiiiD, r);
 IREE_VM_ABI_DECLARE_SHIM(riCrD, r);
 IREE_VM_ABI_DECLARE_SHIM(rii, i);