@@ -579,6 +579,29 @@ def _is_input_reduction_call(expr: ast.AST | None, params: set[str], torch_alias
579579 )
580580
581581
582+ def _is_param_t_call (expr : ast .AST | None , param : str ) -> bool :
583+ return (
584+ isinstance (expr , ast .Call )
585+ and isinstance (expr .func , ast .Attribute )
586+ and expr .func .attr == "t"
587+ and isinstance (expr .func .value , ast .Name )
588+ and expr .func .value .id == param
589+ and not expr .args
590+ and not expr .keywords
591+ )
592+
593+
594+ def _is_self_matmul_expr (expr : ast .AST | None , params : set [str ]) -> bool :
595+ if isinstance (expr , ast .BinOp ) and isinstance (expr .op , ast .MatMult ):
596+ return any (
597+ isinstance (expr .left , ast .Name )
598+ and expr .left .id == param
599+ and _is_param_t_call (expr .right , param )
600+ for param in params
601+ )
602+ return False
603+
604+
582605def _lambda_returns_input_float (expr : ast .AST | None ) -> bool :
583606 if not isinstance (expr , ast .Lambda ):
584607 return False
@@ -690,6 +713,40 @@ def _input_reduction_return_from_body(
690713 return saw_return
691714
692715
716+ def _self_matmul_return_from_body (body : list [ast .stmt ], params : set [str ]) -> bool :
717+ aliases : set [str ] = set ()
718+ saw_return = False
719+
720+ for stmt in body :
721+ if isinstance (stmt , (ast .Import , ast .ImportFrom , ast .Pass )):
722+ continue
723+ if (
724+ isinstance (stmt , ast .Expr )
725+ and isinstance (stmt .value , ast .Constant )
726+ and isinstance (stmt .value .value , str )
727+ ):
728+ continue
729+ if (
730+ isinstance (stmt , ast .Assign )
731+ and len (stmt .targets ) == 1
732+ and isinstance (stmt .targets [0 ], ast .Name )
733+ and _is_self_matmul_expr (stmt .value , params )
734+ ):
735+ aliases .add (stmt .targets [0 ].id )
736+ continue
737+ if isinstance (stmt , ast .Return ):
738+ if _is_self_matmul_expr (stmt .value , params ):
739+ saw_return = True
740+ continue
741+ if isinstance (stmt .value , ast .Name ) and stmt .value .id in aliases :
742+ saw_return = True
743+ continue
744+ return False
745+ return False
746+
747+ return saw_return
748+
749+
693750# ---------------------------------------------------------------------------
694751# Detectors
695752# ---------------------------------------------------------------------------
@@ -1592,6 +1649,69 @@ def detect_input_reduction_output(code: str | SubmissionFacts) -> list[dict]:
15921649 return []
15931650
15941651
1652+ def detect_partial_self_matmul_output (code : str | SubmissionFacts ) -> list [dict ]:
1653+ """Pattern: custom_kernel partial wraps helper returning data @ data.t()."""
1654+ facts = ensure_submission_facts (code )
1655+ tree = facts .ast_tree
1656+ if tree is None :
1657+ return []
1658+ helper_names : set [str ] = set ()
1659+ partial_aliases = {"partial" }
1660+ functools_aliases = {"functools" }
1661+
1662+ for node in facts ._imports :
1663+ for alias in node .names :
1664+ if alias .name == "functools" :
1665+ functools_aliases .add (alias .asname or alias .name )
1666+ for node in facts ._import_froms :
1667+ if node .module != "functools" :
1668+ continue
1669+ for alias in node .names :
1670+ if alias .name == "partial" :
1671+ partial_aliases .add (alias .asname or alias .name )
1672+
1673+ for node in ast .walk (tree ):
1674+ if not isinstance (node , (ast .FunctionDef , ast .AsyncFunctionDef )):
1675+ continue
1676+ params = {arg .arg for arg in node .args .args }
1677+ if params and _self_matmul_return_from_body (node .body , params ):
1678+ helper_names .add (node .name )
1679+
1680+ for stmt in tree .body :
1681+ if not isinstance (stmt , ast .Assign ):
1682+ continue
1683+ if not any (isinstance (t , ast .Name ) and is_entrypoint_name (t .id ) for t in stmt .targets ):
1684+ continue
1685+ value = stmt .value
1686+ if not (
1687+ isinstance (value , ast .Call )
1688+ and len (value .args ) == 1
1689+ and isinstance (value .args [0 ], ast .Name )
1690+ and value .args [0 ].id in helper_names
1691+ and not value .keywords
1692+ ):
1693+ continue
1694+ if isinstance (value .func , ast .Name ) and value .func .id in partial_aliases :
1695+ return [{
1696+ "pattern" : "SELF_MATMUL_OUTPUT" ,
1697+ "severity" : "critical" ,
1698+ "evidence" : "custom_kernel partial wraps helper returning data @ data.t()" ,
1699+ }]
1700+ if (
1701+ isinstance (value .func , ast .Attribute )
1702+ and value .func .attr == "partial"
1703+ and isinstance (value .func .value , ast .Name )
1704+ and value .func .value .id in functools_aliases
1705+ ):
1706+ return [{
1707+ "pattern" : "SELF_MATMUL_OUTPUT" ,
1708+ "severity" : "critical" ,
1709+ "evidence" : "custom_kernel functools.partial wraps helper returning data @ data.t()" ,
1710+ }]
1711+
1712+ return []
1713+
1714+
15951715def detect_torch_compile_cache (code : str | SubmissionFacts ) -> list [dict ]:
15961716 """Pattern 11: torch.compile for pipeline graph caching."""
15971717 facts = ensure_submission_facts (code )
@@ -3948,6 +4068,10 @@ class RulePolicy:
39484068 "INPUT_REDUCTION_OUTPUT" , "fake_output" , "hard" , AUTO_FILTER , (),
39494069 (), "keep" ,
39504070 ),
4071+ "SELF_MATMUL_OUTPUT" : RulePolicy (
4072+ "SELF_MATMUL_OUTPUT" , "fake_output" , "hard" , AUTO_FILTER , (),
4073+ (), "keep" ,
4074+ ),
39514075 "TORCH_COMPILE_CACHE" : RulePolicy (
39524076 "TORCH_COMPILE_CACHE" , "performance_heuristic" , "telemetry" , TELEMETRY_ONLY , (),
39534077 (), "downgrade" ,
@@ -4206,6 +4330,7 @@ def support_only_patterns(matched_patterns: list[dict]) -> bool:
42064330 detect_trivial_probe ,
42074331 detect_input_passthrough_output ,
42084332 detect_input_reduction_output ,
4333+ detect_partial_self_matmul_output ,
42094334 detect_torch_compile_cache ,
42104335 detect_cuda_graph_python ,
42114336 detect_hardcoded_shapes ,
@@ -4246,6 +4371,7 @@ def support_only_patterns(matched_patterns: list[dict]) -> bool:
42464371 ("trivial_probe" , detect_trivial_probe ),
42474372 ("input_passthrough_output" , detect_input_passthrough_output ),
42484373 ("input_reduction_output" , detect_input_reduction_output ),
4374+ ("partial_self_matmul_output" , detect_partial_self_matmul_output ),
42494375 ("torch_compile_cache" , detect_torch_compile_cache ),
42504376 ("cuda_graph_python" , detect_cuda_graph_python ),
42514377 ("hardcoded_shapes" , detect_hardcoded_shapes ),
0 commit comments