Skip to content

Commit 08a6f7a

Browse files
mcr229facebook-github-bot
authored andcommitted
Handle XNNHeader in XNNPACK Runtime (#1543)
Summary: We introduce XNNHeader on runtime side to handle the newly introduced XNNHeader ahead of time. XNNHeader manages the offsets and sizes of the flatbuffer payload and the constant data payload so that it is accessible by the XNNCompiler It is important to note that on serialization side, we have not yet switched our serialization method to `serialize_xnnpack_binary` so this does not yet use the new serialization format. However, passing tests on this illustrates BC as old models will still be able to run on this new runtime. Passing tests here show that the Header Magic correctly works in discerning between using the XNNHeader and the Flatbuffer header Differential Revision: D52556131
1 parent 0962b8a commit 08a6f7a

File tree

3 files changed

+219
-7
lines changed

3 files changed

+219
-7
lines changed

backends/xnnpack/runtime/XNNCompiler.cpp

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
#include <executorch/backends/xnnpack/runtime/XNNCompiler.h>
10+
#include <executorch/backends/xnnpack/runtime/XNNHeader.h>
1011
#include <executorch/backends/xnnpack/schema_generated.h>
1112
#include <executorch/backends/xnnpack/threadpool/threadpool.h>
1213
#include <executorch/runtime/core/exec_aten/util/scalar_type_util.h>
@@ -113,6 +114,7 @@ Error defineTensor(
113114
std::unordered_map<uint32_t, uint32_t>& remapped_ids,
114115
ValuePtr value,
115116
GraphPtr flatbuffer_graph,
117+
const uint8_t* constant_data_ptr,
116118
XNNExecutor* executor,
117119
MemoryAllocator* runtime_allocator) {
118120
const fb_xnnpack::XNNTensorValue* tensor_value = nullptr;
@@ -151,11 +153,20 @@ Error defineTensor(
151153

152154
// Get Pointer to constant data from flatbuffer, if its non-constant
153155
// it is a nullptr
154-
const auto& constant_buffer = *flatbuffer_graph->constant_buffer();
155156
auto buffer_idx = tensor_value->constant_buffer_idx();
156-
const auto buffer_ptr = buffer_idx == 0
157-
? nullptr
158-
: constant_buffer[buffer_idx]->storage()->data();
157+
const uint8_t* buffer_ptr = nullptr;
158+
if (buffer_idx) {
159+
if (!constant_data_ptr) {
160+
const auto& constant_buffer = *flatbuffer_graph->constant_buffer();
161+
buffer_ptr = constant_buffer[buffer_idx]->storage()->data();
162+
} else {
163+
const auto& constant_data_offsets = *flatbuffer_graph->constant_data();
164+
uint64_t constant_data_offset =
165+
constant_data_offsets[buffer_idx]->offset();
166+
buffer_ptr = constant_data_ptr + constant_data_offset;
167+
}
168+
}
169+
159170
xnn_status status;
160171
// The type we might have to convert to
161172
auto dq_datatype = getDataType(tensor_value->dq_datatype());
@@ -1429,14 +1440,30 @@ __ET_NODISCARD Error XNNCompiler::compileModel(
14291440
size_t num_bytes,
14301441
XNNExecutor* executor,
14311442
MemoryAllocator* runtime_allocator) {
1443+
Result<XNNHeader> header = XNNHeader::Parse(buffer_pointer, num_bytes);
1444+
const uint8_t* flatbuffer_data =
1445+
reinterpret_cast<const uint8_t*>(buffer_pointer);
1446+
const uint8_t* constant_data = nullptr;
1447+
1448+
// Header status can only either be Error::Ok or Error::NotFound
1449+
if (header.ok()) {
1450+
flatbuffer_data = reinterpret_cast<const uint8_t*>(buffer_pointer) +
1451+
header->flatbuffer_offset;
1452+
constant_data = reinterpret_cast<const uint8_t*>(buffer_pointer) +
1453+
header->constant_data_offset;
1454+
} else if (header.error() != Error::NotFound) {
1455+
ET_LOG(Error, "XNNHeader may be corrupt");
1456+
return header.error();
1457+
}
1458+
14321459
ET_CHECK_OR_RETURN_ERROR(
1433-
fb_xnnpack::XNNGraphBufferHasIdentifier(buffer_pointer),
1460+
fb_xnnpack::XNNGraphBufferHasIdentifier(flatbuffer_data),
14341461
DelegateInvalidCompatibility,
14351462
"XNNPACK Delegate Serialization Format version identifier '%.4s' != expected '%.4s'",
1436-
flatbuffers::GetBufferIdentifier(buffer_pointer),
1463+
flatbuffers::GetBufferIdentifier(flatbuffer_data),
14371464
fb_xnnpack::XNNGraphIdentifier());
14381465

1439-
auto flatbuffer_graph = fb_xnnpack::GetXNNGraph(buffer_pointer);
1466+
auto flatbuffer_graph = fb_xnnpack::GetXNNGraph(flatbuffer_data);
14401467
// initialize xnnpack
14411468
xnn_status status = xnn_initialize(/*allocator =*/nullptr);
14421469
ET_CHECK_OR_RETURN_ERROR(
@@ -1476,6 +1503,7 @@ __ET_NODISCARD Error XNNCompiler::compileModel(
14761503
remapped_ids,
14771504
value,
14781505
flatbuffer_graph,
1506+
constant_data,
14791507
executor,
14801508
runtime_allocator);
14811509

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#include <executorch/backends/xnnpack/runtime/XNNHeader.h>
10+
11+
#include <cinttypes>
12+
#include <cstring>
13+
14+
#include <executorch/runtime/core/error.h>
15+
#include <executorch/runtime/core/result.h>
16+
17+
#pragma clang diagnostic ignored "-Wdeprecated"
18+
19+
namespace torch {
20+
namespace executor {
21+
namespace xnnpack {
22+
namespace delegate {
23+
24+
namespace {
25+
26+
/// The expected location of the header length field relative to the beginning
27+
/// of the header.
28+
static constexpr size_t kHeaderLengthOffset =
29+
XNNHeader::kMagicOffset + XNNHeader::kMagicSize;
30+
31+
/// The expected location of the flatbuffer data offset field relative to the
32+
/// beginning of the header.
33+
static constexpr size_t kFlatbufferDataOffsetOffset =
34+
kHeaderLengthOffset + sizeof(uint16_t);
35+
36+
/// The expected location of the flatbuffer data size field relative to the
37+
/// beginning of the header.
38+
static constexpr size_t kFlatbufferDataSizeOffset =
39+
kFlatbufferDataOffsetOffset + sizeof(uint32_t);
40+
41+
/// The expected location of the constant data offset field relative to the
42+
/// beginning of the header.
43+
static constexpr size_t kConstantDataOffsetOffset =
44+
kFlatbufferDataSizeOffset + sizeof(uint32_t);
45+
46+
/// The expected location of the constant data size field relative to the
47+
/// beginning of the header.
48+
static constexpr size_t kConstantDataSizeOffset =
49+
kConstantDataOffsetOffset + sizeof(uint32_t);
50+
51+
/// Interprets the 8 bytes at `data` as a little-endian uint64_t.
52+
uint64_t GetUInt64LE(const uint8_t* data) {
53+
return (uint64_t)data[0] | ((uint64_t)data[1] << 8) |
54+
((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) |
55+
((uint64_t)data[4] << 32) | ((uint64_t)data[5] << 40) |
56+
((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56);
57+
}
58+
59+
/// Interprets the 4 bytes at `data` as a little-endian uint32_t.
60+
uint32_t GetUInt32LE(const uint8_t* data) {
61+
return (uint32_t)data[0] | ((uint32_t)data[1] << 8) |
62+
((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24);
63+
}
64+
65+
} // namespace
66+
67+
Result<XNNHeader> XNNHeader::Parse(const void* data, size_t size) {
68+
const uint8_t* header_data = (const uint8_t*)data;
69+
70+
const uint8_t* magic_start = header_data + XNNHeader::kMagicOffset;
71+
if (std::memcmp(magic_start, XNNHeader::kMagic, XNNHeader::kMagicSize) != 0) {
72+
return Error::NotFound;
73+
}
74+
75+
uint32_t flatbuffer_offset =
76+
GetUInt32LE(header_data + kFlatbufferDataOffsetOffset);
77+
78+
uint32_t flatbuffer_size =
79+
GetUInt32LE(header_data + kFlatbufferDataSizeOffset);
80+
81+
uint32_t constant_data_offset =
82+
GetUInt32LE(header_data + kConstantDataOffsetOffset);
83+
84+
uint64_t constant_data_size =
85+
GetUInt64LE(header_data + kConstantDataSizeOffset);
86+
87+
return XNNHeader{
88+
flatbuffer_offset,
89+
flatbuffer_size,
90+
constant_data_offset,
91+
constant_data_size};
92+
}
93+
94+
// Define storage for the static.
95+
constexpr char XNNHeader::kMagic[kMagicSize];
96+
97+
} // namespace delegate
98+
} // namespace xnnpack
99+
} // namespace executor
100+
} // namespace torch

backends/xnnpack/runtime/XNNHeader.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <executorch/runtime/core/result.h>
12+
13+
namespace torch {
14+
namespace executor {
15+
namespace xnnpack {
16+
namespace delegate {
17+
18+
/**
19+
* An extended XNNPACK-header that is embeded before the flatbuffer payload
20+
*
21+
*/
22+
struct XNNHeader {
23+
/**
24+
* The magic offset. This offset is the same as the offset for flatbuffer
25+
* header so we will be able to check if the header is is either the
26+
* flatbuffer head or the wrapper header we introduce here
27+
*/
28+
static constexpr size_t kMagicOffset = 4;
29+
30+
/**
31+
* The magic bytes that identify the header.
32+
*
33+
* This is the canonical definition of the expected value. If the header
34+
* layout ever changes in a compatibility-breaking way, increment the digits
35+
* in the magic. But, doing so will prevent older binaries from recognizing
36+
* the presence of the header. The compatibility-preserving way to make
37+
* changes is to increase the header's length field and add new fields at the
38+
* end.
39+
*/
40+
static constexpr size_t kMagicSize = 4;
41+
static constexpr char kMagic[kMagicSize] = {'X', 'H', '0', '0'};
42+
43+
/**
44+
* The size in bytes of the header length. We store 2 bytes for the header
45+
* length
46+
*/
47+
static constexpr size_t kHeaderLengthSize = 2;
48+
49+
/**
50+
* Look for and parse an ExtendedHeader in the provided data.
51+
*
52+
* @param[in] data The contents of the beginning of the serialized binary
53+
* Program data, starting at offset 0 (i.e., the head of the file).
54+
* @param[in] size Length of `data` in bytes.
55+
*
56+
* @returns an XNNHeader if the header was found and is valid. Returns an
57+
* error if size was too short, if the header was not found, or if the
58+
* header appeared to be corrupt.
59+
*/
60+
static Result<XNNHeader> Parse(const void* data, size_t size);
61+
62+
/**
63+
* The offset in bytes to the beginning of the flatbuffer data.
64+
*/
65+
uint32_t flatbuffer_offset;
66+
/**
67+
* The size in bytes of the flatbuffer data.
68+
*/
69+
uint32_t flatbuffer_size;
70+
71+
/**
72+
* The offset in bytes to the beginning of the constant data.
73+
*/
74+
uint32_t constant_data_offset;
75+
/**
76+
* The size in bytes of the constant data.
77+
*/
78+
uint64_t constant_data_size;
79+
};
80+
81+
} // namespace delegate
82+
} // namespace xnnpack
83+
} // namespace executor
84+
} // namespace torch

0 commit comments

Comments
 (0)