Skip to content

Commit a1e9a35

Browse files
committed
Add vm.buffer.hash and util.buffer.hash ops
This adds support for computing the SipHash24 of a byte range of a buffer, with the goal of using this as a primitive for building a hash table out of VM IR.
1 parent f0c8380 commit a1e9a35

File tree

18 files changed

+326
-4
lines changed

18 files changed

+326
-4
lines changed

compiler/src/iree/compiler/Dialect/Util/IR/UtilOps.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -1506,6 +1506,25 @@ void BufferStoreOp::setSubrangeOperand(unsigned operandIndex,
15061506
getLengthMutable().assign(operand.length);
15071507
}
15081508

1509+
SubrangeOperand BufferHashOp::getSubrangeOperand(unsigned operandIndex) {
1510+
if (operandIndex == 0) {
1511+
return SubrangeOperand{getSource(), getSourceSize(), getSourceOffset(),
1512+
getLength()};
1513+
} else {
1514+
assert(false && "only source is a subrange");
1515+
return {};
1516+
}
1517+
}
1518+
1519+
void BufferHashOp::setSubrangeOperand(unsigned operandIndex,
1520+
SubrangeOperand operand) {
1521+
assert(operandIndex == 0 && "only source is a subrange");
1522+
getSourceMutable().assign(operand.resource);
1523+
getSourceSizeMutable().assign(operand.resourceSize);
1524+
getSourceOffsetMutable().assign(operand.offset);
1525+
getLengthMutable().assign(operand.length);
1526+
}
1527+
15091528
} // namespace mlir::iree_compiler::IREE::Util
15101529

15111530
#define GET_OP_CLASSES

compiler/src/iree/compiler/Dialect/Util/IR/UtilOps.td

+34
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,40 @@ def Util_BufferStoreOp : Util_Op<"buffer.store", [
13411341
}];
13421342
}
13431343

1344+
def Util_BufferHashOp : Util_Op<"buffer.hash", [
1345+
MemoryEffects<[MemRead]>,
1346+
Util_SizeAwareOp,
1347+
DeclareOpInterfaceMethods<Util_SubrangeOperandOpInterface>,
1348+
]> {
1349+
let summary = [{computes the hash of a byte range of a buffer}];
1350+
let description = [{
1351+
Computes the SipHash-2-4 of a value at a byte offset with the given length.
1352+
This always uses a seed of `0x0001020304...0e0f` and produces a single 64
1353+
bit value.
1354+
}];
1355+
1356+
let arguments = (ins
1357+
Util_BufferType:$source,
1358+
Util_Size:$source_size,
1359+
Util_Offset:$source_offset,
1360+
Util_Size:$length
1361+
);
1362+
let results = (outs
1363+
I64:$result
1364+
);
1365+
1366+
let assemblyFormat = [{
1367+
$source `[` $source_offset `for` $length `]`
1368+
`:` type($source) `` `{` $source_size `}` `->` type($result)
1369+
attr-dict-with-keyword
1370+
}];
1371+
1372+
let extraClassDeclaration = [{
1373+
Value getOperandSize(unsigned idx) { return getSourceSize(); }
1374+
Value getResultSize(unsigned idx) { return {}; }
1375+
}];
1376+
}
1377+
13441378
} // OpGroupBufferOps
13451379

13461380
//===----------------------------------------------------------------------===//

compiler/src/iree/compiler/Dialect/Util/IR/test/buffer_ops.mlir

+11
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,14 @@ func.func @buffer_store(%arg0: !util.buffer, %arg1: index, %arg2: i32) {
126126
util.buffer.store %arg2, %arg0[%c100 for %c4] : i32 -> !util.buffer{%arg1}
127127
return
128128
}
129+
130+
// -----
131+
132+
// CHECK-LABEL: @buffer_hash
133+
func.func @buffer_hash(%arg0: !util.buffer, %arg1: index) -> i64 {
134+
%c17 = arith.constant 17 : index
135+
%c100 = arith.constant 100 : index
136+
// CHECK: = util.buffer.hash %arg0[%c100 for %c17] : !util.buffer{%arg1} -> i64
137+
%0 = util.buffer.hash %arg0[%c100 for %c17] : !util.buffer{%arg1} -> i64
138+
return %0 : i64
139+
}

compiler/src/iree/compiler/Dialect/VM/Conversion/UtilToVM/ConvertBufferOps.cpp

+19-1
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,22 @@ struct BufferStoreOpConversion
318318
}
319319
};
320320

321+
struct BufferHashOpConversion
322+
: public OpConversionPattern<IREE::Util::BufferHashOp> {
323+
using OpConversionPattern::OpConversionPattern;
324+
LogicalResult
325+
matchAndRewrite(IREE::Util::BufferHashOp hashOp, OpAdaptor adaptor,
326+
ConversionPatternRewriter &rewriter) const override {
327+
auto newType =
328+
getTypeConverter()->convertType(hashOp.getResult().getType());
329+
auto byteOffset = castToI64(adaptor.getSourceOffset(), rewriter);
330+
auto length = castToI64(adaptor.getLength(), rewriter);
331+
rewriter.replaceOpWithNewOp<IREE::VM::BufferHashOp>(
332+
hashOp, newType, adaptor.getSource(), byteOffset, length);
333+
return success();
334+
}
335+
};
336+
321337
} // namespace
322338

323339
void populateUtilBufferToVMPatterns(MLIRContext *context,
@@ -341,7 +357,8 @@ void populateUtilBufferToVMPatterns(MLIRContext *context,
341357
IREE::Util::BufferDeallocOp, IREE::Util::BufferSliceOp,
342358
IREE::Util::BufferSizeOp, IREE::Util::BufferCopyOp,
343359
IREE::Util::BufferCompareOp, IREE::Util::BufferFillOp,
344-
IREE::Util::BufferLoadOp, IREE::Util::BufferStoreOp>();
360+
IREE::Util::BufferLoadOp, IREE::Util::BufferStoreOp,
361+
IREE::Util::BufferHashOp>();
345362

346363
patterns.insert<BufferConstantOpConversion>(typeConverter, context);
347364
patterns.insert<BufferAllocOpConversion>(typeConverter, context);
@@ -353,6 +370,7 @@ void populateUtilBufferToVMPatterns(MLIRContext *context,
353370
patterns.insert<BufferFillOpConversion>(typeConverter, context);
354371
patterns.insert<BufferLoadOpConversion>(typeConverter, context);
355372
patterns.insert<BufferStoreOpConversion>(typeConverter, context);
373+
patterns.insert<BufferHashOpConversion>(typeConverter, context);
356374
}
357375

358376
} // namespace mlir::iree_compiler

compiler/src/iree/compiler/Dialect/VM/Conversion/UtilToVM/test/buffer_ops.mlir

+17
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,20 @@ func.func @buffer_store_index(%arg0: !util.buffer, %arg1: index, %arg2: index) {
295295
util.buffer.store %arg2, %arg0[%byte_offset for %element_size] : index -> !util.buffer{%arg1}
296296
return
297297
}
298+
299+
// -----
300+
301+
// CHECK-LABEL: @buffer_hash
302+
func.func @buffer_hash(%arg0: !util.buffer, %arg1: index) -> i64 {
303+
%byte_offset = arith.constant 128 : index
304+
%length = arith.constant 17 : index
305+
// CHECK-32-DAG: %[[BYTE_OFFSET:.+]] = vm.const.i64 128
306+
// CHECK-32-DAG: %[[LENGTH:.+]] = vm.const.i64 17
307+
// CHECK-32: %[[VALUE:.+]] = vm.buffer.hash %arg0, %[[BYTE_OFFSET]], %[[LENGTH]] : !vm.buffer -> i64
308+
// CHECK-64-DAG: %[[BYTE_OFFSET:.+]] = vm.const.i64 128
309+
// CHECK-64-DAG: %[[LENGTH:.+]] = vm.const.i64 17
310+
// CHECK-64: %[[VALUE:.+]] = vm.buffer.hash %arg0, %[[BYTE_OFFSET]], %[[LENGTH]] : !vm.buffer -> i64
311+
%0 = util.buffer.hash %arg0[%byte_offset for %length] : !util.buffer{%arg1} -> i64
312+
// CHECK: return %[[VALUE]]
313+
return %0 : i64
314+
}

compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/ConvertVMToEmitC.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -4518,6 +4518,8 @@ void populateVMToEmitCPatterns(ConversionTarget &conversionTarget,
45184518
DenseSet<size_t>({0}), true);
45194519
ADD_CONTAINER_PATTERN(IREE::VM::BufferStoreI64Op, "vm_buffer_store_i64",
45204520
DenseSet<size_t>({0}), true);
4521+
ADD_CONTAINER_PATTERN(IREE::VM::BufferHashOp, "iree_vm_buffer_hash",
4522+
DenseSet<size_t>({0}), true);
45214523
ADD_CONTAINER_PATTERN(IREE::VM::ListReserveOp, "iree_vm_list_reserve",
45224524
DenseSet<size_t>({0}), true);
45234525
ADD_CONTAINER_PATTERN(IREE::VM::ListResizeOp, "iree_vm_list_resize",

compiler/src/iree/compiler/Dialect/VM/Conversion/VMToEmitC/test/buffer_ops.mlir

+13
Original file line numberDiff line numberDiff line change
@@ -236,3 +236,16 @@ vm.module @my_module {
236236
vm.return
237237
}
238238
}
239+
240+
// -----
241+
242+
vm.module @my_module {
243+
// CHECK-LABEL: @my_module_buffer_hash
244+
vm.func @buffer_hash(%buf : !vm.buffer) {
245+
// CHECK: %[[STATUS:.+]] = emitc.call_opaque "iree_vm_buffer_hash"
246+
%c0 = vm.const.i64 0
247+
%c10 = vm.const.i64 10
248+
%v0 = vm.buffer.hash %buf, %c0, %c10 : !vm.buffer -> i64
249+
vm.return
250+
}
251+
}

compiler/src/iree/compiler/Dialect/VM/IR/VMOpcodesCore.td

+3-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class VM_OPC_EnumAttr<string name, string enumName, string enumTag,
4747
string opcodeEnumTag = enumTag;
4848
}
4949

50-
// Next available opcode: 0x84
50+
// Next available opcode: 0x85
5151

5252
// Globals:
5353
def VM_OPC_GlobalLoadI32 : VM_OPC<0x00, "GlobalLoadI32">;
@@ -222,6 +222,7 @@ def VM_OPC_BufferFillI8 : VM_OPC<0x71, "BufferFillI8">;
222222
def VM_OPC_BufferFillI16 : VM_OPC<0x72, "BufferFillI16">;
223223
def VM_OPC_BufferFillI32 : VM_OPC<0x73, "BufferFillI32">;
224224
def VM_OPC_BufferFillI64 : VM_OPC<0x74, "BufferFillI64">;
225+
def VM_OPC_BufferHash : VM_OPC<0x84, "BufferHash">;
225226

226227
// Extension prefixes:
227228
def VM_OPC_PrefixExtF32 : VM_OPC<0xE0, "PrefixExtF32">;
@@ -380,6 +381,7 @@ def VM_CoreOpcodeAttr :
380381
VM_OPC_BufferFillI64,
381382
VM_OPC_BufferCopy,
382383
VM_OPC_BufferCompare,
384+
VM_OPC_BufferHash,
383385

384386
VM_OPC_Block,
385387

compiler/src/iree/compiler/Dialect/VM/IR/VMOps.td

+32
Original file line numberDiff line numberDiff line change
@@ -1598,6 +1598,38 @@ def VM_BufferStoreF64Op :
15981598
let summary = [{64-bit floating-point store}];
15991599
}
16001600

1601+
def VM_BufferHashOp : VM_Op<"buffer.hash", [
1602+
DeclareOpInterfaceMethods<VM_SerializableOpInterface>,
1603+
MemoryEffects<[MemRead]>,
1604+
]> {
1605+
let description = [{
1606+
Computes the SipHash-2-4 of the source buffer at the given offset for
1607+
|length| bytes using seed `0x0001020304...0e0f`.
1608+
}];
1609+
1610+
let arguments = (ins
1611+
VM_RefOf<VM_BufferType>:$source_buffer,
1612+
VM_BufferIndex:$source_offset,
1613+
VM_BufferIndex:$length
1614+
);
1615+
let results = (outs
1616+
I64:$result
1617+
);
1618+
1619+
let assemblyFormat = [{
1620+
$source_buffer `,` $source_offset `,` $length
1621+
attr-dict `:` type($source_buffer) `->` type($result)
1622+
}];
1623+
1624+
let encoding = [
1625+
VM_EncOpcode<VM_OPC_BufferHash>,
1626+
VM_EncOperand<"source_buffer", 0>,
1627+
VM_EncOperand<"source_offset", 1>,
1628+
VM_EncOperand<"length", 2>,
1629+
VM_EncResult<"result">,
1630+
];
1631+
}
1632+
16011633
} // OpGroupBufferOps
16021634

16031635
//===----------------------------------------------------------------------===//

runtime/src/iree/vm/BUILD.bazel

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ iree_runtime_cc_library(
7878
],
7979
deps = [
8080
"//runtime/src/iree/base",
81+
"//runtime/src/iree/base:core_headers",
8182
"//runtime/src/iree/base/internal",
8283
"//runtime/src/iree/base/internal:synchronization",
8384
],

runtime/src/iree/vm/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ iree_cc_library(
6565
"stack.c"
6666
DEPS
6767
iree::base
68+
iree::base::core_headers
6869
iree::base::internal
6970
iree::base::internal::synchronization
7071
PUBLIC

runtime/src/iree/vm/buffer.c

+85
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <stddef.h>
1010
#include <string.h>
1111

12+
#include "iree/base/alignment.h"
1213
#include "iree/vm/instance.h"
1314

1415
IREE_VM_DEFINE_TYPE_ADAPTERS(iree_vm_buffer, iree_vm_buffer_t);
@@ -314,6 +315,90 @@ IREE_API_EXPORT iree_status_t iree_vm_buffer_write_elements(
314315
return iree_ok_status();
315316
}
316317

318+
// Based on reference implementation from https://github.com/veorq/SipHash
319+
// By Jean-Philippe Aumasson and Daniel J. Bernstein.
320+
#define ROTL(x, b) (uint64_t)(((x) << (b)) | ((x) >> (64 - (b))))
321+
#define SIPROUND(v0, v1, v2, v3) \
322+
v0 += v1; \
323+
v1 = ROTL(v1, 13); \
324+
v1 ^= v0; \
325+
v0 = ROTL(v0, 32); \
326+
v2 += v3; \
327+
v3 = ROTL(v3, 16); \
328+
v3 ^= v2; \
329+
v0 += v3; \
330+
v3 = ROTL(v3, 21); \
331+
v3 ^= v0; \
332+
v2 += v1; \
333+
v1 = ROTL(v1, 17); \
334+
v1 ^= v2; \
335+
v2 = ROTL(v2, 32)
336+
337+
// Using SipHash-2-4.
338+
#ifndef cROUNDS
339+
#define cROUNDS 2
340+
#endif
341+
#ifndef dROUNDS
342+
#define dROUNDS 4
343+
#endif
344+
345+
IREE_API_EXPORT iree_status_t iree_vm_buffer_hash(
346+
const iree_vm_buffer_t* source_buffer, iree_host_size_t source_offset,
347+
iree_host_size_t length, int64_t* result) {
348+
IREE_TRACE_ZONE_BEGIN(z0);
349+
IREE_ASSERT_ARGUMENT(source_buffer);
350+
// Get the byte span for the source data.
351+
iree_const_byte_span_t source_span = iree_const_byte_span_empty();
352+
IREE_RETURN_AND_END_ZONE_IF_ERROR(
353+
z0, iree_vm_buffer_map_ro(source_buffer, source_offset, length, 1,
354+
&source_span));
355+
const uint8_t* source = source_span.data;
356+
const uint8_t* end = source + source_span.data_length -
357+
(source_span.data_length % sizeof(uint64_t));
358+
const int left = source_span.data_length & 7;
359+
uint64_t hash = ((uint64_t)source_span.data_length) << 56;
360+
361+
// Using key = 0x000102030405060708090a0b0c0d0e0f
362+
uint64_t v0 = UINT64_C(0x736f6d6570736575 ^ 0x0706050403020100);
363+
uint64_t v1 = UINT64_C(0x646f72616e646f6d ^ 0x0f0e0d0c0b0a0908);
364+
uint64_t v2 = UINT64_C(0x6c7967656e657261 ^ 0x0706050403020100);
365+
uint64_t v3 = UINT64_C(0x7465646279746573 ^ 0x0f0e0d0c0b0a0908);
366+
uint64_t m;
367+
368+
for (; source != end; source += 8) {
369+
m = iree_unaligned_load_le_u64((const uint64_t*)source);
370+
v3 ^= m;
371+
for (int i = 0; i < cROUNDS; ++i) {
372+
SIPROUND(v0, v1, v2, v3);
373+
}
374+
v0 ^= m;
375+
}
376+
377+
uint64_t tmp = 0;
378+
for (int l = left; l > 0; --l) {
379+
tmp = tmp << 8;
380+
tmp |= (uint64_t)source[l - 1];
381+
}
382+
hash |= tmp;
383+
v3 ^= hash;
384+
385+
for (int i = 0; i < cROUNDS; ++i) {
386+
SIPROUND(v0, v1, v2, v3);
387+
}
388+
389+
v0 ^= hash;
390+
v2 ^= 0xff;
391+
392+
for (int i = 0; i < dROUNDS; ++i) {
393+
SIPROUND(v0, v1, v2, v3);
394+
}
395+
396+
hash = v0 ^ v1 ^ v2 ^ v3;
397+
iree_unaligned_store_le_u64(result, hash);
398+
IREE_TRACE_ZONE_END(z0);
399+
return iree_ok_status();
400+
}
401+
317402
iree_status_t iree_vm_buffer_register_types(iree_vm_instance_t* instance) {
318403
static const iree_vm_ref_type_descriptor_t descriptor = {
319404
.destroy = iree_vm_buffer_destroy,

runtime/src/iree/vm/buffer.h

+6
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ IREE_API_EXPORT iree_status_t iree_vm_buffer_write_elements(
199199
iree_host_size_t target_offset, iree_host_size_t element_count,
200200
iree_host_size_t element_length);
201201

202+
// Computes the SipHash-2-4 of the source buffer along the byte range starting
203+
// as |source_offset| of |length| bytes. Uses 128-bit key `0x00010203...0e0f`.
204+
IREE_API_EXPORT iree_status_t iree_vm_buffer_hash(
205+
const iree_vm_buffer_t* source_buffer, iree_host_size_t source_offset,
206+
iree_host_size_t length, int64_t* result);
207+
202208
// Low-level helper for accessing a typed view of a buffer for read access.
203209
// The calling function must be safe to return from. Assumes buffer is non-null.
204210
// Prefer iree_vm_buffer_read_elements for larger reads.

runtime/src/iree/vm/bytecode/disassembler.c

+21
Original file line numberDiff line numberDiff line change
@@ -1024,6 +1024,27 @@ iree_status_t iree_vm_bytecode_disassemble_op(
10241024
break;
10251025
}
10261026

1027+
DISASM_OP(CORE, BufferHash) {
1028+
bool buffer_is_move;
1029+
uint16_t buffer_reg =
1030+
VM_ParseOperandRegRef("source_buffer", &buffer_is_move);
1031+
uint16_t offset_reg = VM_ParseOperandRegI64("source_offset");
1032+
uint16_t length_reg = VM_ParseOperandRegI64("length");
1033+
uint16_t result_reg = VM_ParseResultRegI64("result");
1034+
EMIT_I32_REG_NAME(result_reg);
1035+
IREE_RETURN_IF_ERROR(
1036+
iree_string_builder_append_cstring(b, " = vm.buffer.hash "));
1037+
EMIT_REF_REG_NAME(buffer_reg);
1038+
EMIT_OPTIONAL_VALUE_REF(&regs->ref[buffer_reg]);
1039+
IREE_RETURN_IF_ERROR(iree_string_builder_append_cstring(b, ", "));
1040+
EMIT_I64_REG_NAME(offset_reg);
1041+
EMIT_OPTIONAL_VALUE_I64(regs->i32[offset_reg]);
1042+
IREE_RETURN_IF_ERROR(iree_string_builder_append_cstring(b, ", "));
1043+
EMIT_I64_REG_NAME(length_reg);
1044+
EMIT_OPTIONAL_VALUE_I64(regs->i32[length_reg]);
1045+
break;
1046+
}
1047+
10271048
//===------------------------------------------------------------------===//
10281049
// Lists
10291050
//===------------------------------------------------------------------===//

0 commit comments

Comments
 (0)