|
8 | 8 | import os
|
9 | 9 | import tempfile
|
10 | 10 |
|
11 |
| -from dataclasses import fields, is_dataclass |
| 11 | +from dataclasses import dataclass, fields, is_dataclass |
| 12 | +from typing import ClassVar, Literal |
12 | 13 |
|
13 | 14 | import pkg_resources
|
14 | 15 | from executorch.backends.xnnpack.serialization.xnnpack_graph_schema import XNNGraph
|
15 | 16 | from executorch.exir._serialize._dataclass import _DataclassEncoder
|
16 | 17 |
|
17 | 18 | from executorch.exir._serialize._flatbuffer import _flatc_compile
|
18 | 19 |
|
| 20 | +# Byte order of numbers written to program headers. Always little-endian |
| 21 | +# regardless of the host system, since all commonly-used modern CPUs are little |
| 22 | +# endian. |
| 23 | +_HEADER_BYTEORDER: Literal["little"] = "little" |
| 24 | + |
19 | 25 |
|
20 | 26 | def sanity_check_xnngraph_dataclass(table, name: str = ""):
|
21 | 27 | """
|
@@ -68,6 +74,76 @@ def check_for_sym(obj, name):
|
68 | 74 | check_for_sym(o, _name_field)
|
69 | 75 |
|
70 | 76 |
|
| 77 | +@dataclass |
| 78 | +class XNNPACKHeader: |
| 79 | + # Class Constants |
| 80 | + |
| 81 | + # magic bytes that should be at the beginning of the header |
| 82 | + EXPECTED_MAGIC: ClassVar[bytes] = b"XH00" |
| 83 | + # The length of the header in bytes. |
| 84 | + EXPECTED_LENGTH: ClassVar[int] = ( |
| 85 | + # Zeros magic |
| 86 | + # We offset the magic by 4 bytes so that it is in the same location |
| 87 | + # as the flatbuffer payload's magic. This way we can dynamically |
| 88 | + # choose between the XNNPACK Header and Flatbuffer Header |
| 89 | + 4 |
| 90 | + # Header magic |
| 91 | + + 4 |
| 92 | + # Header Length |
| 93 | + + 4 |
| 94 | + # Flatbuffer offset |
| 95 | + + 8 |
| 96 | + # Flatbuffer size |
| 97 | + + 8 |
| 98 | + # Constant Data offset |
| 99 | + + 8 |
| 100 | + # Constant Data size |
| 101 | + + 8 |
| 102 | + ) |
| 103 | + |
| 104 | + # Instance attributes. @dataclass will turn these into ctor args. |
| 105 | + |
| 106 | + # offset to the flatbuffer data |
| 107 | + flatbuffer_offset: int |
| 108 | + |
| 109 | + # flatbuffer size |
| 110 | + flatbuffer_size: int |
| 111 | + |
| 112 | + # offset to the constant data |
| 113 | + constant_data_offset: int |
| 114 | + |
| 115 | + # constant data size |
| 116 | + constant_data_size: int |
| 117 | + |
| 118 | + def to_bytes(self) -> bytes: |
| 119 | + """ |
| 120 | + Returns the binary representation of the XNNPACK Header. |
| 121 | + """ |
| 122 | + |
| 123 | + data: bytes = ( |
| 124 | + # Padding for magic bytes. This is so that header magic is in the same position |
| 125 | + # as the flatbuffer magic, and allows consumer to detect whether the header is |
| 126 | + # being used or not |
| 127 | + b"\x00\x00\x00\x00" |
| 128 | + # XNNPACK Header's magic. This allows consumer to detect whether or not the header |
| 129 | + # is being used or the flatbuffer header is being used |
| 130 | + + self.EXPECTED_MAGIC |
| 131 | + # uint32_t: Size of this header. This makes it easier to add new fields to the header |
| 132 | + # in the future. |
| 133 | + + self.EXPECTED_LENGTH.to_bytes(4, byteorder=_HEADER_BYTEORDER) |
| 134 | + # uint64_t: Offset to the start of the flatbuffer data |
| 135 | + + self.flatbuffer_offset.to_bytes(8, byteorder=_HEADER_BYTEORDER) |
| 136 | + # uint64_t: Size of the flatbuffer data payload |
| 137 | + + self.flatbuffer_size.to_bytes(8, byteorder=_HEADER_BYTEORDER) |
| 138 | + # uint64_t: Offset to the start of the constant data |
| 139 | + + self.constant_data_offset.to_bytes(8, byteorder=_HEADER_BYTEORDER) |
| 140 | + # uint64_t: Size of the constant data |
| 141 | + + self.constant_data_size.to_bytes(8, byteorder=_HEADER_BYTEORDER) |
| 142 | + ) |
| 143 | + |
| 144 | + return data |
| 145 | + |
| 146 | + |
71 | 147 | def convert_to_flatbuffer(xnnpack_graph: XNNGraph) -> bytes:
|
72 | 148 | sanity_check_xnngraph_dataclass(xnnpack_graph)
|
73 | 149 | xnnpack_graph_json = json.dumps(xnnpack_graph, cls=_DataclassEncoder)
|
|
0 commit comments