@@ -35,6 +35,7 @@ class Properties:
3535 pure : bool
3636 uses_opcode : bool
3737 needs_guard_ip : bool
38+ unpredictable_jump : bool
3839 tier : int | None = None
3940 const_oparg : int = - 1
4041 needs_prev : bool = False
@@ -76,7 +77,8 @@ def from_list(properties: list["Properties"]) -> "Properties":
7677 pure = all (p .pure for p in properties ),
7778 needs_prev = any (p .needs_prev for p in properties ),
7879 no_save_ip = all (p .no_save_ip for p in properties ),
79- needs_guard_ip = any (p .needs_guard_ip for p in properties )
80+ needs_guard_ip = any (p .needs_guard_ip for p in properties ),
81+ unpredictable_jump = any (p .unpredictable_jump for p in properties ),
8082 )
8183
8284 @property
@@ -105,6 +107,7 @@ def infallible(self) -> bool:
105107 pure = True ,
106108 no_save_ip = False ,
107109 needs_guard_ip = False ,
110+ unpredictable_jump = False ,
108111)
109112
110113
@@ -887,6 +890,42 @@ def stmt_escapes(stmt: Stmt) -> bool:
887890 else :
888891 assert False , "Unexpected statement type"
889892
893+ def stmt_has_jump_on_unpredictable_path_body (stmts : list [Stmt ] | None , branches_seen : int ) -> bool :
894+ if not stmts :
895+ return False , branches_seen
896+ predict = False
897+ seen = 0
898+ for st in stmts :
899+ predict_body , seen_body = stmt_has_jump_on_unpredictable_path (st , branches_seen )
900+ predict = predict or predict_body
901+ seen += seen_body
902+ return predict , seen
903+
904+ def stmt_has_jump_on_unpredictable_path (stmt : Stmt , branches_seen : int ) -> bool :
905+ if isinstance (stmt , BlockStmt ):
906+ return stmt_has_jump_on_unpredictable_path_body (stmt .body , branches_seen )
907+ elif isinstance (stmt , SimpleStmt ):
908+ for tkn in stmt .contents :
909+ if tkn .text == "JUMPBY" :
910+ return True , branches_seen
911+ return False , branches_seen
912+ elif isinstance (stmt , IfStmt ):
913+ return True , branches_seen + 1
914+ elif isinstance (stmt , MacroIfStmt ):
915+ predict , seen = stmt_has_jump_on_unpredictable_path_body (stmt .body , branches_seen )
916+ if stmt .else_body :
917+ predict_else , seen_else = stmt_has_jump_on_unpredictable_path_body (stmt .else_body , branches_seen )
918+ return predict != predict_else , seen + seen_else
919+ return predict , seen
920+ elif isinstance (stmt , ForStmt ):
921+ unpredictable , branches_seen = stmt_has_jump_on_unpredictable_path (stmt .body , branches_seen )
922+ return unpredictable , branches_seen + 1
923+ elif isinstance (stmt , WhileStmt ):
924+ unpredictable , branches_seen = stmt_has_jump_on_unpredictable_path (stmt .body , branches_seen )
925+ return unpredictable , branches_seen + 1
926+ else :
927+ assert False , f"Unexpected statement type { stmt } "
928+
890929
891930def compute_properties (op : parser .CodeDef ) -> Properties :
892931 escaping_calls = find_escaping_api_calls (op )
@@ -914,6 +953,8 @@ def compute_properties(op: parser.CodeDef) -> Properties:
914953 escapes = stmt_escapes (op .block )
915954 pure = False if isinstance (op , parser .LabelDef ) else "pure" in op .annotations
916955 no_save_ip = False if isinstance (op , parser .LabelDef ) else "no_save_ip" in op .annotations
956+ unpredictable , branches_seen = stmt_has_jump_on_unpredictable_path (op .block , 0 )
957+ unpredictable_jump = False if isinstance (op , parser .LabelDef ) else (unpredictable and branches_seen > 0 )
917958 return Properties (
918959 escaping_calls = escaping_calls ,
919960 escapes = escapes ,
@@ -938,6 +979,7 @@ def compute_properties(op: parser.CodeDef) -> Properties:
938979 tier = tier_variable (op ),
939980 needs_prev = variable_used (op , "prev_instr" ),
940981 needs_guard_ip = variable_used (op , "TIER2_STORE_IP" ) or variable_used (op , "LLTRACE_RESUME_FRAME" ) or variable_used (op , "DISPATCH_INLINED" ),
982+ unpredictable_jump = unpredictable_jump ,
941983 )
942984
943985def expand (items : list [StackItem ], oparg : int ) -> list [StackItem ]:
0 commit comments