11# Copyright (c) Meta Platforms, Inc. and affiliates.
22# All rights reserved.
3- # Copyright 2025 Arm Limited and/or its affiliates.
3+ # Copyright 2025-2026 Arm Limited and/or its affiliates.
44#
55# This source code is licensed under the BSD-style license found in the
66# LICENSE file in the root directory of this source tree.
99
1010# TODO(T138924864): Refactor to unify the serialization for bundled program and executorch program.
1111
12+ import functools
1213import importlib .resources as _resources
1314import json
1415import os
16+ import re
1517import tempfile
18+ from typing import Any
1619
1720import executorch .devtools .bundled_program .schema as bp_schema
1821
1922import executorch .devtools .bundled_program .serialize as serialization_package
23+
24+ import flatbuffers # pyre-ignore[21]
2025from executorch .devtools .bundled_program .core import BundledProgram
26+ from executorch .devtools .bundled_program .serialize .generated .bundled_program_flatbuffer import (
27+ Bool as _Bool ,
28+ BundledMethodTestCase as _BundledMethodTestCase ,
29+ BundledMethodTestSuite as _BundledMethodTestSuite ,
30+ BundledProgram as _BundledProgram ,
31+ Double as _Double ,
32+ Int as _Int ,
33+ Tensor as _Tensor ,
34+ Value as _Value ,
35+ ValueUnion as _ValueUnion ,
36+ )
2137from executorch .exir ._serialize ._dataclass import _DataclassEncoder , _json_to_dataclass
2238from executorch .exir ._serialize ._flatbuffer import _flatc_compile , _flatc_decompile
39+ from executorch .exir ._serialize ._flatbuffer_program import (
40+ _coerce_bytes ,
41+ _create_aligned_byte_vector ,
42+ )
2343
2444# The prefix of schema files used for bundled program
2545BUNDLED_PROGRAM_SCHEMA_NAME = "bundled_program_schema"
2646SCALAR_TYPE_SCHEMA_NAME = "scalar_type"
2747
2848
49+ @functools .lru_cache (maxsize = 1 )
50+ def _bundled_program_file_identifier () -> bytes :
51+ schema = _resources .read_binary (
52+ serialization_package , f"{ BUNDLED_PROGRAM_SCHEMA_NAME } .fbs"
53+ )
54+ match = re .search (rb'file_identifier\s+"([^"]+)"' , schema )
55+ if match is None :
56+ raise ValueError (
57+ f"Missing file_identifier in { BUNDLED_PROGRAM_SCHEMA_NAME } .fbs"
58+ )
59+ file_identifier = match .group (1 )
60+ if len (file_identifier ) != 4 :
61+ raise ValueError (
62+ f"Invalid file_identifier length { len (file_identifier )} "
63+ f"in { BUNDLED_PROGRAM_SCHEMA_NAME } .fbs"
64+ )
65+ return file_identifier
66+
67+
2968def write_schema (d : str , schema_name : str ) -> None :
3069 schema_path = os .path .join (d , "{}.fbs" .format (schema_name ))
3170 with open (schema_path , "wb" ) as schema_file :
@@ -78,6 +117,145 @@ def convert_from_flatbuffer(program_flatbuffer: bytes) -> bytes:
78117 return output_file .read ()
79118
80119
120+ def _pack_tensor (self : Any , builder : Any ) -> int :
121+ if self .sizes is not None :
122+ _Tensor .TensorStartSizesVector (builder , len (self .sizes ))
123+ for i in reversed (range (len (self .sizes ))):
124+ builder .PrependInt32 (self .sizes [i ])
125+ sizes = builder .EndVector ()
126+ if self .data is not None :
127+ data = _create_aligned_byte_vector (builder , _coerce_bytes (self .data ), 16 )
128+ if self .dimOrder is not None :
129+ dim_order = _create_aligned_byte_vector (
130+ builder , _coerce_bytes (self .dimOrder ), 1
131+ )
132+
133+ _Tensor .TensorStart (builder )
134+ _Tensor .TensorAddScalarType (builder , self .scalarType )
135+ if self .sizes is not None :
136+ _Tensor .TensorAddSizes (builder , sizes )
137+ if self .data is not None :
138+ _Tensor .TensorAddData (builder , data )
139+ if self .dimOrder is not None :
140+ _Tensor .TensorAddDimOrder (builder , dim_order )
141+ return _Tensor .TensorEnd (builder )
142+
143+
144+ def _pack_bundled_program (self : Any , builder : Any ) -> int :
145+ if self .methodTestSuites is not None :
146+ method_test_suites_list = [
147+ method_test_suite .Pack (builder )
148+ for method_test_suite in self .methodTestSuites
149+ ]
150+ _BundledProgram .BundledProgramStartMethodTestSuitesVector (
151+ builder , len (self .methodTestSuites )
152+ )
153+ for i in reversed (range (len (self .methodTestSuites ))):
154+ builder .PrependUOffsetTRelative (method_test_suites_list [i ])
155+ method_test_suites = builder .EndVector ()
156+ if self .program is not None :
157+ program = _create_aligned_byte_vector (builder , _coerce_bytes (self .program ), 32 )
158+
159+ _BundledProgram .BundledProgramStart (builder )
160+ _BundledProgram .BundledProgramAddVersion (builder , self .version )
161+ if self .methodTestSuites is not None :
162+ _BundledProgram .BundledProgramAddMethodTestSuites (builder , method_test_suites )
163+ if self .program is not None :
164+ _BundledProgram .BundledProgramAddProgram (builder , program )
165+ return _BundledProgram .BundledProgramEnd (builder )
166+
167+
168+ @functools .lru_cache (maxsize = 1 )
169+ def _install_fast_packers () -> None :
170+ _Tensor .TensorT .Pack = _pack_tensor
171+ _BundledProgram .BundledProgramT .Pack = _pack_bundled_program
172+
173+
174+ def _convert_tensor (val : bp_schema .Tensor ) -> Any :
175+ result = _Tensor .TensorT ()
176+ result .scalarType = int (val .scalar_type )
177+ result .sizes = list (val .sizes )
178+ result .data = _coerce_bytes (val .data )
179+ result .dimOrder = _coerce_bytes (val .dim_order )
180+ return result
181+
182+
183+ def _convert_int (val : bp_schema .Int ) -> Any :
184+ result = _Int .IntT ()
185+ result .intVal = val .int_val
186+ return result
187+
188+
189+ def _convert_bool (val : bp_schema .Bool ) -> Any :
190+ result = _Bool .BoolT ()
191+ result .boolVal = val .bool_val
192+ return result
193+
194+
195+ def _convert_double (val : bp_schema .Double ) -> Any :
196+ result = _Double .DoubleT ()
197+ result .doubleVal = val .double_val
198+ return result
199+
200+
201+ def _convert_value_union (val : bp_schema .ValueUnion ) -> tuple [int , Any ]:
202+ if isinstance (val , bp_schema .Tensor ):
203+ return _ValueUnion .ValueUnion .Tensor , _convert_tensor (val )
204+ if isinstance (val , bp_schema .Int ):
205+ return _ValueUnion .ValueUnion .Int , _convert_int (val )
206+ if isinstance (val , bp_schema .Bool ):
207+ return _ValueUnion .ValueUnion .Bool , _convert_bool (val )
208+ if isinstance (val , bp_schema .Double ):
209+ return _ValueUnion .ValueUnion .Double , _convert_double (val )
210+ return _ValueUnion .ValueUnion .NONE , None
211+
212+
213+ def _convert_value (val : bp_schema .Value ) -> Any :
214+ result = _Value .ValueT ()
215+ result .valType , result .val = _convert_value_union (val .val )
216+ return result
217+
218+
219+ def _convert_method_test_case (val : bp_schema .BundledMethodTestCase ) -> Any :
220+ result = _BundledMethodTestCase .BundledMethodTestCaseT ()
221+ result .inputs = [_convert_value (value ) for value in val .inputs ]
222+ result .expectedOutputs = [_convert_value (value ) for value in val .expected_outputs ]
223+ return result
224+
225+
226+ def _convert_method_test_suite (val : bp_schema .BundledMethodTestSuite ) -> Any :
227+ result = _BundledMethodTestSuite .BundledMethodTestSuiteT ()
228+ result .methodName = val .method_name
229+ result .testCases = [
230+ _convert_method_test_case (test_case ) for test_case in val .test_cases
231+ ]
232+ return result
233+
234+
235+ def _convert_bundled_program (val : bp_schema .BundledProgram ) -> Any :
236+ result = _BundledProgram .BundledProgramT ()
237+ result .version = val .version
238+ result .methodTestSuites = [
239+ _convert_method_test_suite (suite ) for suite in val .method_test_suites
240+ ]
241+ result .program = _coerce_bytes (val .program )
242+ return result
243+
244+
245+ def _bundled_program_schema_to_flatbuffer (
246+ bundled_program : bp_schema .BundledProgram ,
247+ ) -> bytes :
248+ _install_fast_packers ()
249+ bundled_program_t = _convert_bundled_program (bundled_program )
250+ builder = flatbuffers .Builder ()
251+ bundled_program_offset = bundled_program_t .Pack (builder )
252+ builder .Finish (
253+ bundled_program_offset ,
254+ file_identifier = _bundled_program_file_identifier (),
255+ )
256+ return bytes (builder .Output ())
257+
258+
81259# from bundled program to flatbuffer
82260def serialize_from_bundled_program_to_flatbuffer (
83261 bundled_program : BundledProgram ,
@@ -94,9 +272,7 @@ def serialize_from_bundled_program_to_flatbuffer(
94272
95273 bundled_program_in_schema = bundled_program .serialize_to_schema ()
96274
97- return convert_to_flatbuffer (
98- serialize_from_bundled_program_to_json (bundled_program_in_schema )
99- )
275+ return _bundled_program_schema_to_flatbuffer (bundled_program_in_schema )
100276
101277
102278# From flatbuffer to bundled program in schema.
0 commit comments