Skip to content

Commit e022b96

Browse files
fix(detector): detect partial self matmul output
1 parent a88c107 commit e022b96

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

‎kernelguard.py‎

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
582605
def _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+
15951715
def 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

Comments
 (0)