5050
5151@dataclass (frozen = True )
5252class CudaPteInput :
53- """One native CUDA export and its optional external tensor data."""
53+ """One CUDA PTE input and its optional external tensor data."""
5454
5555 pte_path : Path
5656 ptd_path : Optional [Path ]
@@ -377,6 +377,54 @@ def _compact_delegate_data(program: Program) -> None:
377377 program .backend_delegate_data = payloads
378378
379379
380+ def _select_merge_fallback (
381+ inherited_fallbacks : Sequence [Tuple [_Artifact , CudaAotiVariant ]],
382+ fallback_artifact : Optional [_Artifact ],
383+ fallback_delegates : Optional [Dict [Tuple [str , int ], CudaAotiMetadata ]],
384+ reference : _Artifact ,
385+ reference_metadata : CudaAotiMetadata ,
386+ identity : Tuple [str , int ],
387+ ) -> Optional [Tuple [_Artifact , CudaAotiVariant ]]:
388+ if fallback_artifact is not None :
389+ assert fallback_delegates is not None
390+ metadata = fallback_delegates [identity ]
391+ _validate_shared_weights (
392+ reference , reference_metadata , fallback_artifact , metadata , identity
393+ )
394+ if len (metadata .variants ) != 1 or not metadata .variants [0 ].ptx_compute :
395+ raise ValueError (
396+ f"Fallback PTE { fallback_artifact .source .pte_path } must contain "
397+ f"exactly one PTX-capable variant for delegate { identity } "
398+ )
399+ return fallback_artifact , replace (metadata .variants [0 ], fallback_only = True )
400+
401+ if not inherited_fallbacks :
402+ return None
403+ fallback_source , fallback = inherited_fallbacks [0 ]
404+ try :
405+ fallback_data = fallback_source .pte_named_data [fallback .so_blob_key ]
406+ except KeyError as error :
407+ raise ValueError (
408+ f"{ fallback_source .source .pte_path } does not contain CUDA SO "
409+ f"{ fallback .so_blob_key !r} "
410+ ) from error
411+ for candidate_source , candidate in inherited_fallbacks [1 :]:
412+ try :
413+ candidate_data = candidate_source .pte_named_data [candidate .so_blob_key ]
414+ except KeyError as error :
415+ raise ValueError (
416+ f"{ candidate_source .source .pte_path } does not contain CUDA SO "
417+ f"{ candidate .so_blob_key !r} "
418+ ) from error
419+ if candidate != fallback or candidate_data != fallback_data :
420+ raise ValueError (
421+ f"CUDA fallback variants differ for delegate { identity } between "
422+ f"{ fallback_source .source .pte_path } and "
423+ f"{ candidate_source .source .pte_path } "
424+ )
425+ return fallback_source , fallback
426+
427+
380428def _merge_delegate_variants (
381429 regular_artifacts : Sequence [_Artifact ],
382430 regular_delegates : Sequence [Dict [Tuple [str , int ], CudaAotiMetadata ]],
@@ -389,13 +437,17 @@ def _merge_delegate_variants(
389437) -> List [CudaAotiVariant ]:
390438 variants = []
391439 target_sms = set ()
440+ inherited_fallbacks : List [Tuple [_Artifact , CudaAotiVariant ]] = []
392441 reference = regular_artifacts [0 ]
393442 for artifact , delegates in zip (regular_artifacts , regular_delegates ):
394443 metadata = delegates [identity ]
395444 _validate_shared_weights (
396445 reference , reference_metadata , artifact , metadata , identity
397446 )
398447 for variant in metadata .variants :
448+ if variant .fallback_only :
449+ inherited_fallbacks .append ((artifact , variant ))
450+ continue
399451 if variant .target_sm in target_sms :
400452 raise ValueError (f"Duplicate CUDA target sm{ variant .target_sm } " )
401453 target_sms .add (variant .target_sm )
@@ -407,7 +459,7 @@ def _merge_delegate_variants(
407459 f"{ variant .so_blob_key !r} "
408460 ) from error
409461 merged_store .add_named_data (variant .so_blob_key , so_data )
410- variants .append (replace (variant , ptx_compute = 0 , fallback_only = False ))
462+ variants .append (replace (variant , ptx_compute = 0 ))
411463 provenance .append (
412464 CudaPteProvenance (
413465 delegate = identity ,
@@ -419,39 +471,36 @@ def _merge_delegate_variants(
419471 )
420472
421473 variants .sort (key = lambda variant : variant .target_sm )
422- if fallback_artifact is not None :
423- assert fallback_delegates is not None
424- metadata = fallback_delegates [identity ]
425- _validate_shared_weights (
426- reference , reference_metadata , fallback_artifact , metadata , identity
427- )
428- fallback_variants = [
429- variant for variant in metadata .variants if variant .ptx_compute
430- ]
431- if len (fallback_variants ) != 1 :
432- raise ValueError (
433- f"Fallback PTE { fallback_artifact .source .pte_path } must contain "
434- f"exactly one PTX-capable variant for delegate { identity } "
435- )
436- fallback = replace (fallback_variants [0 ], fallback_only = True )
437- try :
438- so_data = fallback_artifact .pte_named_data [fallback .so_blob_key ]
439- except KeyError as error :
440- raise ValueError (
441- f"{ fallback_artifact .source .pte_path } does not contain CUDA SO "
442- f"{ fallback .so_blob_key !r} "
443- ) from error
444- merged_store .add_named_data (fallback .so_blob_key , so_data )
445- variants .append (fallback )
446- provenance .append (
447- CudaPteProvenance (
448- delegate = identity ,
449- kind = "ptx-fallback" ,
450- target_sm = fallback .target_sm ,
451- ptx_compute = fallback .ptx_compute ,
452- source_pte = fallback_artifact .source .pte_path ,
453- )
474+ fallback_selection = _select_merge_fallback (
475+ inherited_fallbacks ,
476+ fallback_artifact ,
477+ fallback_delegates ,
478+ reference ,
479+ reference_metadata ,
480+ identity ,
481+ )
482+ if fallback_selection is None :
483+ return variants
484+ fallback_source , fallback = fallback_selection
485+
486+ try :
487+ so_data = fallback_source .pte_named_data [fallback .so_blob_key ]
488+ except KeyError as error :
489+ raise ValueError (
490+ f"{ fallback_source .source .pte_path } does not contain CUDA SO "
491+ f"{ fallback .so_blob_key !r} "
492+ ) from error
493+ merged_store .add_named_data (fallback .so_blob_key , so_data )
494+ variants .append (fallback )
495+ provenance .append (
496+ CudaPteProvenance (
497+ delegate = identity ,
498+ kind = "ptx-fallback" ,
499+ target_sm = fallback .target_sm ,
500+ ptx_compute = fallback .ptx_compute ,
501+ source_pte = fallback_source .source .pte_path ,
454502 )
503+ )
455504 return variants
456505
457506
@@ -490,14 +539,32 @@ def _prepare_merged_output(reference: _Artifact) -> Tuple[Program, NamedDataStor
490539
491540 merged_store = NamedDataStore ()
492541 if reference .pte .named_data is not None :
493- merged_store .merge_named_data_store (reference .pte .named_data )
542+ cuda_so_keys = _cuda_so_keys (reference )
543+ merged_store .merge_named_data_store (
544+ NamedDataStoreOutput (
545+ buffers = reference .pte .named_data .buffers ,
546+ pte_data = {
547+ key : entry
548+ for key , entry in reference .pte .named_data .pte_data .items ()
549+ if key not in cuda_so_keys
550+ },
551+ external_data = {
552+ filename : {
553+ key : entry
554+ for key , entry in entries .items ()
555+ if key not in cuda_so_keys
556+ }
557+ for filename , entries in reference .pte .named_data .external_data .items ()
558+ },
559+ )
560+ )
494561 return merged_program , merged_store
495562
496563
497564def merge_cuda_pte_files_with_provenance (
498565 inputs : Sequence [CudaPteInput ], fallback : Optional [CudaPteInput ] = None
499566) -> CudaPteMergeResult :
500- """Merge exact-SM CUDA exports and an optional PTX-only fallback source."""
567+ """Merge CUDA variants and an optional PTX-capable fallback source."""
501568 if torch .version .hip is not None :
502569 raise RuntimeError (
503570 "CUDA PTE merging supports only NVIDIA CUDA and is not supported on ROCm"
@@ -584,16 +651,16 @@ def merge_cuda_pte_files(
584651
585652def _parse_args () -> argparse .Namespace :
586653 parser = argparse .ArgumentParser (
587- description = ("Merge exact-SM CUDA PTEs with an optional explicit PTX fallback" )
654+ description = ("Merge CUDA PTE variants with an optional explicit PTX fallback" )
588655 )
589656 parser .add_argument (
590657 "--input-pte" ,
591658 action = "append" ,
592659 required = True ,
593660 type = Path ,
594661 help = (
595- "Regular CUDA PTE contributing exact-SM native cubins; the first "
596- "input supplies common data"
662+ "CUDA PTE contributing native variants and, for a previous merge output, "
663+ "an inherited fallback; the first input supplies common data"
597664 ),
598665 )
599666 parser .add_argument (
@@ -606,7 +673,10 @@ def _parse_args() -> argparse.Namespace:
606673 parser .add_argument (
607674 "--fallback-pte" ,
608675 type = Path ,
609- help = "Single CUDA PTE contributing only the PTX runtime fallback" ,
676+ help = (
677+ "CUDA PTE containing exactly one PTX-capable variant to use as the "
678+ "runtime fallback"
679+ ),
610680 )
611681 parser .add_argument (
612682 "--fallback-ptd" ,
0 commit comments