@@ -598,11 +598,14 @@ def _model_has_uint8_io(self, model: torch.fx.GraphModule) -> bool:
598598 self ._is_uint8_quantized_io_boundary (node ) for node in model .graph .nodes
599599 )
600600
601- def _get_shared_clique (self , root_node : Node ) -> tuple [set [Node ], list [Any ], bool ]:
601+ def _get_shared_clique (
602+ self , root_node : Node
603+ ) -> tuple [set [Node ], list [Any ], bool , bool ]:
602604 shared_nodes = set ()
603605 bfs_queue = [root_node ]
604606 adjacent_qspecs : list [Any ] = []
605607 touches_quantized_io = False
608+ touches_uint8_quantized_io = False
606609
607610 while bfs_queue :
608611 node = bfs_queue .pop (0 )
@@ -612,13 +615,24 @@ def _get_shared_clique(self, root_node: Node) -> tuple[set[Node], list[Any], boo
612615 self ._maybe_enqueue_shared_node (input_node , shared_nodes , bfs_queue )
613616 self ._append_output_qspec (input_node , adjacent_qspecs )
614617 touches_quantized_io |= self ._is_quantized_io_boundary (input_node )
618+ touches_uint8_quantized_io |= self ._is_uint8_quantized_io_boundary (
619+ input_node
620+ )
615621
616622 for output_node in node .users .keys ():
617623 self ._maybe_enqueue_shared_node (output_node , shared_nodes , bfs_queue )
618624 self ._append_input_qspec (output_node , node , adjacent_qspecs )
619625 touches_quantized_io |= self ._is_quantized_io_boundary (output_node )
626+ touches_uint8_quantized_io |= self ._is_uint8_quantized_io_boundary (
627+ output_node
628+ )
620629
621- return shared_nodes , adjacent_qspecs , touches_quantized_io
630+ return (
631+ shared_nodes ,
632+ adjacent_qspecs ,
633+ touches_quantized_io ,
634+ touches_uint8_quantized_io ,
635+ )
622636
623637 def _should_skip_while_shared_qspec (self , node : Node ) -> bool :
624638 return node .target == torch .ops .higher_order .while_loop and bool (
@@ -673,9 +687,12 @@ def _annotate_shared_cluster(self, root_node: Node) -> None:
673687 )
674688 return
675689
676- shared_nodes , adjacent_qspecs , touches_quantized_io = self ._get_shared_clique (
677- root_node
678- )
690+ (
691+ shared_nodes ,
692+ adjacent_qspecs ,
693+ touches_quantized_io ,
694+ touches_uint8_quantized_io ,
695+ ) = self ._get_shared_clique (root_node )
679696
680697 # If there is no neighbor qspec to propagate but the cluster sits on the
681698 # quantized I/O boundary (e.g. a state-passthrough cat whose only neighbors
@@ -695,6 +712,15 @@ def _annotate_shared_cluster(self, root_node: Node) -> None:
695712 node_order = {node : index for index , node in enumerate (root_node .graph .nodes )}
696713 ordered_nodes = sorted (shared_nodes , key = lambda node : node_order .get (node , 0 ))
697714
715+ if touches_uint8_quantized_io and any (
716+ node .target in self ._UINT8_IO_BRIDGE_OPS for node in shared_nodes
717+ ):
718+ self .report_reject (
719+ ordered_nodes ,
720+ "Shared-qspec bridge cluster touches uint8 model IO." ,
721+ )
722+ return
723+
698724 if self ._annotate_while_with_additional_inputs (root_node , adjacent_qspecs ):
699725 return
700726
@@ -734,20 +760,9 @@ def _annotate_shared_cluster(self, root_node: Node) -> None:
734760 return
735761
736762 def annotate (self , model : torch .fx .GraphModule ) -> None : # type: ignore[override]
737- targets = self .targets
738- if self ._model_has_uint8_io (model ):
739- targets = [
740- target for target in targets if target not in self ._UINT8_IO_BRIDGE_OPS
741- ]
742-
743- original_targets = self .targets
744- self .targets = targets
745- try :
746- for node in model .graph .nodes :
747- if node .target in self .targets and not self ._is_annotated (node ):
748- self ._annotate_shared_cluster (node )
749- finally :
750- self .targets = original_targets
763+ for node in model .graph .nodes :
764+ if node .target in self .targets and not self ._is_annotated (node ):
765+ self ._annotate_shared_cluster (node )
751766
752767 def validate (self , model : torch .fx .GraphModule ) -> bool : # type: ignore[override]
753768 return True
0 commit comments