4848from executorch .exir .passes .executorch_prim_ops_registry import is_sym_op
4949from executorch .exir .print_program import _stacktrace_to_framelist , inspect_node
5050from executorch .exir .schema import (
51+ AllocationDetails ,
5152 BackendDelegate ,
5253 BackendDelegateDataReference ,
5354 BackendDelegateInlineData ,
@@ -328,6 +329,59 @@ def _emit_list(self, val: List[_Argument], val_type: _SchemaType) -> EValue:
328329 ExportErrorType .NOT_SUPPORTED , f"Unknown list type: { val_type } "
329330 )
330331
332+ def _get_allocation_info (self , spec : TensorSpec ) -> AllocationDetails :
333+ """Returns the allocation info for a given TensorSpec."""
334+ self ._internal_assert_emitter (
335+ isinstance (spec .mem_id , int ) and spec .mem_id >= 0 ,
336+ self .node ,
337+ f"Non-const tensor should be an activation tensor: mem_id { spec .mem_id } " ,
338+ )
339+
340+ self ._internal_assert_emitter (
341+ isinstance (spec .mem_offset , int ) and spec .mem_offset >= 0 ,
342+ self .node ,
343+ f"Non-const tensor should be an activation tensor: mem_offset { spec .mem_offset } " ,
344+ )
345+ try :
346+ allocation_info = make_allocation_info (spec .mem_id , spec .mem_offset )
347+ except AddressSpaceOverflowException as e :
348+ raise InternalError (
349+ self ._emit_node_specific_error (
350+ self .node ,
351+ (
352+ f"{ e } \n Hint: If you are using a memory pass based on dynamic shape bounds, "
353+ f"such as ConstraintBasedSymShapeEvalPass, this may be the cause of an "
354+ f"unbacked SymInt with its upper bound lazily set to 2^64-1 (uint64 max) "
355+ "during torch.export()."
356+ ),
357+ )
358+ )
359+ return allocation_info
360+
361+ def _save_new_const_tensor (
362+ self ,
363+ spec : TensorSpec ,
364+ buffer_data : bytes ,
365+ hashed : str ,
366+ allocation_info : Optional [AllocationDetails ],
367+ ) -> int :
368+ """Saves a new constant tensor to the constant buffer and returns the buffer idx"""
369+
370+ self .program_state .allocated_specs .append (spec )
371+ # +1 because the first buffer location is reserved.
372+
373+ # Update buffer_idx to point to the end of the list where we are adding the new buffer.
374+ buffer = Buffer (storage = buffer_data )
375+ if allocation_info :
376+ buffer_idx = len (self .program_state .mutable_buffer )
377+ self .program_state .cached_spec_mutable_hash_values [hashed ] = buffer_idx
378+ self .program_state .mutable_buffer .append (buffer )
379+ else :
380+ buffer_idx = len (self .program_state .constant_buffer )
381+ self .program_state .cached_spec_hash_values [hashed ] = buffer_idx
382+ self .program_state .constant_buffer .append (buffer )
383+ return buffer_idx
384+
331385 def _tensor_spec_to_evalue (self , spec : TensorSpec ) -> EValue :
332386 """Constructs an EValue from the given TensorSpec."""
333387
@@ -339,35 +393,12 @@ def _tensor_spec_to_evalue(self, spec: TensorSpec) -> EValue:
339393 # default algos to set offsets, so need to check both.
340394 if spec .mem_id is not None and spec .mem_offset is not None :
341395 # Tensor is an activation.
342- self ._internal_assert_emitter (
343- isinstance (spec .mem_id , int ) and spec .mem_id >= 0 ,
344- self .node ,
345- f"Non-const tensor should be an activation tensor: mem_id { spec .mem_id } " ,
346- )
347-
348- self ._internal_assert_emitter (
349- isinstance (spec .mem_offset , int ) and spec .mem_offset >= 0 ,
350- self .node ,
351- f"Non-const tensor should be an activation tensor: mem_offset { spec .mem_offset } " ,
352- )
353- try :
354- allocation_info = make_allocation_info (spec .mem_id , spec .mem_offset )
355- except AddressSpaceOverflowException as e :
356- raise InternalError (
357- self ._emit_node_specific_error (
358- self .node ,
359- (
360- f"{ e } \n Hint: If you are using a memory pass based on dynamic shape bounds, "
361- f"such as ConstraintBasedSymShapeEvalPass, this may be the cause of an "
362- f"unbacked SymInt with its upper bound lazily set to 2^64-1 (uint64 max) "
363- "during torch.export()."
364- ),
365- )
366- )
396+ allocation_info = self ._get_allocation_info (spec )
367397
398+ # Tensor is either a constant tensor, or a mutable tensor with an initial state.
368399 if spec .const :
369400 # Tensor with a blob we need to serialize. May not actually be constant at runtime
370- # if it's a weight with an associated gradient
401+ # if it's a weight with an associated gradient.
371402 spec_array_type = (
372403 ctypes .c_char * typing .cast (torch .UntypedStorage , spec .storage ).nbytes ()
373404 )
@@ -392,23 +423,11 @@ def _tensor_spec_to_evalue(self, spec: TensorSpec) -> EValue:
392423 else :
393424 buffer_idx = self .program_state .cached_spec_hash_values .get (hashed , - 1 )
394425
395- # Haven't seen this constant before
426+ # Haven't seen this constant before.
396427 if buffer_idx == - 1 :
397- # Update buffer_idx to point to the end of the list where we are adding the new buffer.
398- buffer = Buffer (storage = buffer_data )
399- self .program_state .allocated_specs .append (spec )
400- # +1 because the first buffer location is reserved
401-
402- if allocation_info :
403- buffer_idx = len (self .program_state .mutable_buffer )
404- self .program_state .cached_spec_mutable_hash_values [hashed ] = (
405- buffer_idx
406- )
407- self .program_state .mutable_buffer .append (buffer )
408- else :
409- buffer_idx = len (self .program_state .constant_buffer )
410- self .program_state .cached_spec_hash_values [hashed ] = buffer_idx
411- self .program_state .constant_buffer .append (buffer )
428+ buffer_idx = self ._save_new_const_tensor (
429+ spec , buffer_data , hashed , allocation_info
430+ )
412431
413432 if spec .const and spec .nbytes () != len (buffer_data ):
414433 raise InternalError (
0 commit comments