3333namespace executorch {
3434namespace runtime {
3535
36+ using deserialization::NamedData;
3637using internal::PlatformMemoryAllocator;
3738
3839/* *
@@ -289,6 +290,113 @@ Result<bool> parse_cond_value(const EValue& cond_value) {
289290
290291} // namespace
291292
293+ Result<size_t > Method::get_num_external_constants () {
294+ auto flatbuffer_values = serialization_plan_->values ();
295+ size_t n_value = flatbuffer_values->size ();
296+
297+ size_t n_external_constants = 0 ;
298+ for (size_t i = 0 ; i < n_value; ++i) {
299+ auto serialization_value = flatbuffer_values->Get (i);
300+ // Ensure values are non-null.
301+ // Note that as a side-effect of this check, we're guaranteed that all
302+ // values are non-null, so later loops can skip that check.
303+ ET_CHECK_OR_RETURN_ERROR (
304+ serialization_value != nullptr &&
305+ (serialization_value->val_type () ==
306+ executorch_flatbuffer::KernelTypes::Null ||
307+ serialization_value->val () != nullptr ),
308+ InvalidProgram,
309+ " Null value at index %" ET_PRIsize_t,
310+ i);
311+ // Ignore non-tensor types.
312+ if (serialization_value->val_type () !=
313+ executorch_flatbuffer::KernelTypes::Tensor) {
314+ continue ;
315+ }
316+ const auto s_tensor = static_cast <const executorch_flatbuffer::Tensor*>(
317+ serialization_value->val ());
318+
319+ // An external constant is tagged with EXTERNAL and has no
320+ // allocation_info.
321+ if (s_tensor->extra_tensor_info () != nullptr &&
322+ s_tensor->extra_tensor_info ()->location () ==
323+ executorch_flatbuffer::TensorDataLocation::EXTERNAL &&
324+ s_tensor->allocation_info () == nullptr ) {
325+ n_external_constants++;
326+ }
327+ }
328+ return n_external_constants;
329+ }
330+
331+ Error Method::parse_external_constants (const NamedDataMap* named_data_map) {
332+ auto flatbuffer_values = serialization_plan_->values ();
333+ size_t n_value = flatbuffer_values->size ();
334+
335+ // n_external_constants_ counts the number of successfully-initialized
336+ // external constants for ~Method() to clean up, and is incremented at the
337+ // bottom of the loop. This makes it safe for errors to return without
338+ // updating any state.
339+ n_external_constants_ = 0 ;
340+ for (size_t i = 0 ; i < n_value; ++i) {
341+ auto serialization_value = flatbuffer_values->Get (i);
342+ // Ignore non-tensor types.
343+ if (serialization_value->val_type () !=
344+ executorch_flatbuffer::KernelTypes::Tensor) {
345+ continue ;
346+ }
347+ const auto s_tensor = static_cast <const executorch_flatbuffer::Tensor*>(
348+ serialization_value->val ());
349+ // Constant tensors are resolved here; tensors with allocation_info are
350+ // mutable and are resolved in parse_values.
351+ if (s_tensor->extra_tensor_info () == nullptr ||
352+ s_tensor->extra_tensor_info ()->location () !=
353+ executorch_flatbuffer::TensorDataLocation::EXTERNAL ||
354+ s_tensor->allocation_info () != nullptr ) {
355+ continue ;
356+ }
357+ ET_CHECK_OR_RETURN_ERROR (
358+ s_tensor->extra_tensor_info ()->fully_qualified_name () != nullptr ,
359+ InvalidExternalData,
360+ " Fully qualified name of external tensor is null at index %zu" ,
361+ i);
362+
363+ const char * key =
364+ s_tensor->extra_tensor_info ()->fully_qualified_name ()->c_str ();
365+
366+ // Check if this tensor has already been resolved.
367+ if (get_data_by_key (
368+ key, Span<NamedData>(external_constants_, n_external_constants_)) !=
369+ nullptr ) {
370+ continue ;
371+ }
372+ Result<const TensorLayout> tensor_layout =
373+ named_data_map->get_metadata (key);
374+ if (!tensor_layout.ok ()) {
375+ return tensor_layout.error ();
376+ }
377+ // Check external tensor compatibility.
378+ Error err =
379+ deserialization::validateTensorLayout (s_tensor, tensor_layout.get ());
380+ if (err != Error::Ok) {
381+ return err;
382+ }
383+ // Save the key.
384+ external_constants_[n_external_constants_].key = key;
385+
386+ // Save the buffer.
387+ Result<FreeableBuffer> buffer = named_data_map->get_data (key);
388+ ET_CHECK_OR_RETURN_ERROR (
389+ buffer.ok (),
390+ InvalidExternalData,
391+ " Buffer retrieved from get_data is not valid" );
392+ new (&external_constants_[n_external_constants_].buffer )
393+ FreeableBuffer (std::move (buffer.get ()));
394+
395+ n_external_constants_ += 1 ;
396+ }
397+ return Error::Ok;
398+ }
399+
292400Error Method::parse_values (const NamedDataMap* named_data_map) {
293401 auto flatbuffer_values = serialization_plan_->values ();
294402 ET_CHECK_OR_RETURN_ERROR (
@@ -299,23 +407,37 @@ Error Method::parse_values(const NamedDataMap* named_data_map) {
299407 return Error::MemoryAllocationFailed;
300408 }
301409
410+ // Count the number of tensors marked as EXTERNAL for this method. The actual
411+ // number of external constants may be smaller, eg. if multiple tensors point
412+ // to the same underlying data buffer.
413+ // This function also ensures that all flatbuffer_values entries
414+ // are non-null, so `val_as_X()` calls below are guaranteed to return
415+ // non-null pointers.
416+ Result<size_t > max_external_constants = get_num_external_constants ();
417+ if (!max_external_constants.ok ()) {
418+ return max_external_constants.error ();
419+ }
420+ if (max_external_constants.get () > 0 ) {
421+ // Allocate space for external tensors.
422+ external_constants_ =
423+ memory_manager_->method_allocator ()->allocateList <NamedData>(
424+ max_external_constants.get ());
425+ if (external_constants_ == nullptr ) {
426+ return Error::MemoryAllocationFailed;
427+ }
428+ Error err = parse_external_constants (named_data_map);
429+ if (err != Error::Ok) {
430+ return err;
431+ }
432+ }
433+
302434 // n_value_ counts the number of successfully-initialized values for ~Method()
303435 // to clean up, and is incremented at the bottom of the loop. This makes it
304436 // safe for errors to return without updating any state.
305437 n_value_ = 0 ;
306438
307439 for (size_t i = 0 ; i < n_value; ++i) {
308440 auto serialization_value = flatbuffer_values->Get (i);
309- // Ensure that the `val_as_X()` calls will return non-null pointers.
310- ET_CHECK_OR_RETURN_ERROR (
311- serialization_value != nullptr &&
312- (serialization_value->val_type () ==
313- executorch_flatbuffer::KernelTypes::Null ||
314- serialization_value->val () != nullptr ),
315- InvalidProgram,
316- " Null value at index %" ET_PRIsize_t,
317- i);
318-
319441 const auto val = serialization_value->val ();
320442
321443 switch (serialization_value->val_type ()) {
@@ -416,7 +538,8 @@ Error Method::parse_values(const NamedDataMap* named_data_map) {
416538 program_,
417539 memory_manager_,
418540 static_cast <const executorch_flatbuffer::Tensor*>(val),
419- named_data_map);
541+ named_data_map,
542+ Span<NamedData>(external_constants_, n_external_constants_));
420543 if (!t.ok ()) {
421544 ET_LOG (
422545 Error,
@@ -1496,6 +1619,10 @@ Method::~Method() {
14961619 delegates_[i].~BackendDelegate ();
14971620 }
14981621 }
1622+ // Free resources associated with external constants.
1623+ for (int i = 0 ; i < n_external_constants_; i++) {
1624+ external_constants_[i].buffer .~FreeableBuffer ();
1625+ }
14991626 // All other fields are trivially destructible.
15001627}
15011628} // namespace runtime
0 commit comments