|
48 | 48 | } |
49 | 49 |
|
50 | 50 |
|
| 51 | +def _compute_rule_dep_masks(model: CanonicalModel) -> list[int]: |
| 52 | + """Compute a uint64 bitmask of fact IDs each rule depends on.""" |
| 53 | + masks: list[int] = [] |
| 54 | + cond_offset = 0 |
| 55 | + for r in model.rules: |
| 56 | + when = r.get("when", {}) |
| 57 | + cond_count = 0 |
| 58 | + if isinstance(when, dict): |
| 59 | + for gk in ("all", "any", "not"): |
| 60 | + g = when.get(gk) |
| 61 | + if isinstance(g, list): |
| 62 | + cond_count += len(g) |
| 63 | + mask = 0 |
| 64 | + for ci in range(cond_offset, cond_offset + cond_count): |
| 65 | + if ci < len(model.conditions): |
| 66 | + fid = model.conditions[ci].get("fact_id", 0) |
| 67 | + if fid < 64: |
| 68 | + mask |= 1 << fid |
| 69 | + masks.append(mask) |
| 70 | + cond_offset += cond_count |
| 71 | + return masks |
| 72 | + |
| 73 | + |
| 74 | +def _compute_required_mode( |
| 75 | + rule: dict, |
| 76 | + model: CanonicalModel, |
| 77 | +) -> str: |
| 78 | + """Return the C literal for required_mode. |
| 79 | +
|
| 80 | + For mode_guard rules whose 'when' block contains an equality check |
| 81 | + on a fact named 'mode' (or similar mode fact), extract the mode value. |
| 82 | + Otherwise return UINT16_MAX (any mode). |
| 83 | + """ |
| 84 | + if rule.get("class") != "mode_guard": |
| 85 | + return "UINT16_MAX" |
| 86 | + |
| 87 | + when = rule.get("when", {}) |
| 88 | + if not isinstance(when, dict): |
| 89 | + return "UINT16_MAX" |
| 90 | + |
| 91 | + # Scan all condition groups for mode equality checks |
| 92 | + for gk in ("all", "any"): |
| 93 | + group = when.get(gk) |
| 94 | + if not isinstance(group, list): |
| 95 | + continue |
| 96 | + for cond in group: |
| 97 | + if not isinstance(cond, dict): |
| 98 | + continue |
| 99 | + fact_name = cond.get("fact", "") |
| 100 | + op = cond.get("op", "") |
| 101 | + # Look for mode facts by checking if the fact name contains 'mode' |
| 102 | + if "mode" in fact_name.lower() and op == "==": |
| 103 | + val = cond.get("value") |
| 104 | + if isinstance(val, int): |
| 105 | + return str(val) |
| 106 | + # Value might be a mode name string |
| 107 | + if isinstance(val, str) and val in model.mode_id_map: |
| 108 | + return str(model.mode_id_map[val]) |
| 109 | + return "UINT16_MAX" |
| 110 | + |
| 111 | + |
51 | 112 | def _c_str(s: str | None) -> str: |
52 | 113 | return f'"{s}"' if s else "NULL" |
53 | 114 |
|
@@ -99,6 +160,18 @@ def emit_c_header(model: CanonicalModel, emit_trace_strings: bool = True) -> str |
99 | 160 |
|
100 | 161 | lines.append("") |
101 | 162 |
|
| 163 | + # Per-rule dependency bitmasks for dirty-flag rule skip |
| 164 | + rule_dep_masks = _compute_rule_dep_masks(model) |
| 165 | + if rule_dep_masks: |
| 166 | + lines.append("/* Per-rule input fact dependency bitmasks (dirty-flag skip). */") |
| 167 | + mask_strs = [f"UINT64_C(0x{m:016x})" for m in rule_dep_masks] |
| 168 | + lines.append( |
| 169 | + "#define ARBITER_MODEL_RULE_DEPS { " |
| 170 | + + ", ".join(mask_strs) |
| 171 | + + " }" |
| 172 | + ) |
| 173 | + lines.append("") |
| 174 | + |
102 | 175 | # State defines (REQ-ARCH-039) |
103 | 176 | states = getattr(model, "states", []) |
104 | 177 | if states: |
@@ -223,12 +296,17 @@ def emit_c_source(model: CanonicalModel, header_name: str = "arbiter_model.h", |
223 | 296 |
|
224 | 297 | expr_start = r.get("_expr_start", 0) |
225 | 298 | expr_count = r.get("_expr_count", 0) |
| 299 | + |
| 300 | + # Mode-aware pruning: mode_guard rules with a mode equality check |
| 301 | + required_mode = _compute_required_mode(r, model) |
| 302 | + |
226 | 303 | lines.append( |
227 | 304 | f"\t{{ .id = {i}, .rule_class = {rclass}, " |
228 | 305 | f".condition_start = {cond_offset}, .condition_count = {cond_count}, " |
229 | 306 | f".action_start = {action_start}, .action_count = {action_count}, " |
230 | 307 | f".expr_start = {expr_start}, .expr_count = {expr_count}, " |
231 | 308 | f".safety_goal_id = UINT16_MAX, .set_mode = {set_mode}, " |
| 309 | + f".required_mode = {required_mode}, " |
232 | 310 | f".safety_critical = {safety_critical}, " |
233 | 311 | f".name = {name}, .explanation = {explanation} }}," |
234 | 312 | ) |
|
0 commit comments