@@ -217,7 +217,7 @@ function def_operands(entry::DefEntry)
217217end
218218
219219mutable struct RewriteDriver
220- sci :: StructuredIRCode
220+ rewriter :: Rewriter
221221 defs:: Dict{SSAValue, DefEntry}
222222 dispatch:: Dict{Any, Vector{RewriteRule}}
223223 worklist:: Worklist
@@ -226,47 +226,66 @@ mutable struct RewriteDriver
226226 max_rewrites:: Int
227227end
228228
229- """ Compute fresh use count for an SSA value."""
230- use_count (driver:: RewriteDriver , val:: SSAValue ) =
231- length (uses (driver. sci. entry, val))
232-
233229#= ============================================================================
234- Notifications
230+ Rewriter listener: driver-side bookkeeping reacts to IR mutations
235231=============================================================================#
236232
237- """ Add operand-producing instructions to the worklist (enables cascading)."""
238- function add_operands_to_worklist! (driver:: RewriteDriver , entry:: DefEntry )
239- for op in def_operands (entry)
240- op isa SSAValue || continue
241- haskey (driver. defs, op) && push! (driver. worklist, op)
242- end
233+ # The driver has its own state on top of the Rewriter's index: a `defs` map
234+ # keyed by func, the `worklist`, and a `modified` set for cascading. These
235+ # `notify_*` hooks let the Rewriter keep that state in sync, so the driver
236+ # doesn't need to wrap every mutation callsite. Same pattern as MLIR's
237+ # `RewriterBase::Listener::notifyOperation{Inserted,Modified,Erased}`.
238+
239+ function notify_inserted! (d:: RewriteDriver , block:: Block , inst:: Instruction )
240+ stmt = inst[:stmt ]
241+ call = resolve_call (block, stmt)
242+ call === nothing && return
243+ func, _ = call
244+ val = SSAValue (inst)
245+ d. defs[val] = DefEntry (block, val, func)
246+ push! (d. worklist, val)
243247end
244248
245- """ Add instructions that use `val` to the worklist (their operand changed)."""
246- function add_users_to_worklist! (driver:: RewriteDriver , val:: SSAValue )
247- for inst in users (driver. sci. entry, val)
248- push! (driver. worklist, SSAValue (inst))
249+ function notify_modified! (d:: RewriteDriver , block:: Block , val:: SSAValue ,
250+ @nospecialize (old_stmt), @nospecialize (new_stmt))
251+ # Refresh the def entry so worklist dispatch picks the new func.
252+ call = resolve_call (block, new_stmt)
253+ if call != = nothing
254+ func, _ = call
255+ d. defs[val] = DefEntry (block, val, func)
249256 end
257+ # Worklist cascading is done by the callers (`apply_rewrite!`,
258+ # `apply_inplace_rewrite!`, `commute_arith_transparent`), which re-seed
259+ # `val` and its users themselves.
250260end
251261
252- """ Erase an instruction and notify the worklist."""
253- function erase_op! (driver:: RewriteDriver , entry:: DefEntry )
254- add_operands_to_worklist! (driver, entry)
255- if haskey (entry. block, entry. val. id)
256- delete! (entry. block, entry. val. id)
262+ function notify_erased! (d:: RewriteDriver , :: Block , val:: SSAValue ,
263+ @nospecialize (old_stmt))
264+ # Operand-defs may now be dead; cascade them to the worklist.
265+ if old_stmt isa Expr
266+ for_expr_operands (old_stmt) do op
267+ op isa SSAValue || return
268+ haskey (d. defs, op) && push! (d. worklist, op)
269+ end
257270 end
258- delete! (driver. defs, entry. val)
259- remove! (driver. worklist, entry. val)
271+ delete! (d. defs, val)
272+ remove! (d. worklist, val)
273+ delete! (d. modified, val)
260274end
261275
262- """ Register a newly inserted instruction."""
263- function notify_insert! (driver:: RewriteDriver , block:: Block , inst:: Instruction )
264- val = SSAValue (inst)
265- call = resolve_call (block, inst)
266- call === nothing && return
267- func, _ = call
268- driver. defs[val] = DefEntry (block, val, func)
269- push! (driver. worklist, val)
276+ #= ============================================================================
277+ Driver-side query helpers (thin proxies to the Rewriter)
278+ =============================================================================#
279+
280+ users_of (driver:: RewriteDriver , val:: SSAValue ) = users (driver. rewriter, val)
281+
282+ use_count (driver:: RewriteDriver , val:: SSAValue ) = use_count (driver. rewriter, val)
283+
284+ """ Add instructions that use `val` to the worklist (their operand changed)."""
285+ function add_users_to_worklist! (driver:: RewriteDriver , val:: SSAValue )
286+ for u in users_of (driver, val)
287+ push! (driver. worklist, u)
288+ end
270289end
271290
272291#= ============================================================================
@@ -291,7 +310,7 @@ function merge_bindings!(dest::Dict{Symbol,Any}, src::Dict{Symbol,Any})
291310end
292311
293312function pattern_match (driver:: RewriteDriver , @nospecialize (val), pat:: PCall ,
294- block:: Block = driver. sci. entry)
313+ block:: Block = driver. rewriter . sci. entry)
295314 val isa SSAValue || return nothing
296315 entry = get (driver. defs, val, nothing )
297316 entry === nothing && return nothing
@@ -322,19 +341,19 @@ function pattern_match(driver::RewriteDriver, @nospecialize(val), pat::PCall,
322341 return nothing
323342end
324343
325- pattern_match (driver:: RewriteDriver , @nospecialize (val), pat:: PBind , block:: Block = driver. sci. entry) =
344+ pattern_match (driver:: RewriteDriver , @nospecialize (val), pat:: PBind , block:: Block = driver. rewriter . sci. entry) =
326345 MatchResult (Dict {Symbol,Any} (pat. name => val), SSAValue[])
327346
328347function pattern_match (driver:: RewriteDriver , @nospecialize (val), pat:: PTypedBind ,
329- block:: Block = driver. sci. entry)
348+ block:: Block = driver. rewriter . sci. entry)
330349 T = value_type (block, val)
331350 T === nothing && return nothing
332351 CC. widenconst (T) <: pat.type || return nothing
333352 MatchResult (Dict {Symbol,Any} (pat. name => val), SSAValue[])
334353end
335354
336355function pattern_match (driver:: RewriteDriver , @nospecialize (val), pat:: POneUse ,
337- block:: Block = driver. sci. entry)
356+ block:: Block = driver. rewriter . sci. entry)
338357 val isa SSAValue && use_count (driver, val) == 1 || return nothing
339358 pattern_match (driver, val, pat. inner, block)
340359end
343362# For non-SSA operands (enum constants, predicates): checks ===.
344363# For SSA operands: routed through const_value on the ConstantAnalysis result.
345364function pattern_match (driver:: RewriteDriver , @nospecialize (val), pat:: PLiteral ,
346- block:: Block = driver. sci. entry)
365+ block:: Block = driver. rewriter . sci. entry)
347366 val === pat. val && return MatchResult (Dict {Symbol,Any} (), SSAValue[])
348367 if val isa SSAValue
349368 c = const_value (driver. constants, val)
@@ -384,9 +403,8 @@ function resolve_rhs(driver::RewriteDriver, block, ref, op::RCall, bindings, roo
384403 typ = CC. widenconst (t)
385404 break
386405 end
387- inst = insert_before! (block, ref, Expr (:call , op. func, operands... ), typ;
406+ inst = insert_before! (driver . rewriter, block, ref, Expr (:call , op. func, operands... ), typ;
388407 flag= inferred_flags (op. func))
389- notify_insert! (driver, block, inst)
390408 SSAValue (inst)
391409end
392410
@@ -406,12 +424,8 @@ function apply_inplace_rewrite!(driver::RewriteDriver, block, val::SSAValue, rul
406424 # the flag from the new func's declared effects (`efunc` overrides),
407425 # mirroring inference's `flags_for_effects` — the inferred bits on the
408426 # old call describe the OLD op and don't carry over.
409- if rule. rhs. func === driver. defs[val]. func
410- block[val. id] = (stmt= new_stmt,)
411- else
412- block[val. id] = (stmt= new_stmt, flag= inferred_flags (rule. rhs. func))
413- end
414- driver. defs[val] = DefEntry (block, val, rule. rhs. func)
427+ flag = rule. rhs. func === driver. defs[val]. func ? nothing : inferred_flags (rule. rhs. func)
428+ replace_stmt! (driver. rewriter, block, val, new_stmt; flag)
415429 push! (driver. worklist, val)
416430 add_users_to_worklist! (driver, val)
417431 return true
@@ -438,8 +452,8 @@ function resolve_inplace_rhs(driver, bindings, op::RCall, lhs_op::PCall)
438452 for (sub_rhs, sub_lhs) in zip (op. operands, lhs_op. operands)]
439453 # Sub-call func is enforced equal to the matched lhs func above
440454 # (`op.func === lhs_op.func`), so the flag is still valid — only operands
441- # changed. Partial-NamedTuple setindex preserves type and flag.
442- entry. block[ matched_ssa. id] = (stmt = Expr (:call , op. func, new_ops... ), )
455+ # changed.
456+ replace_stmt! (driver . rewriter, entry. block, matched_ssa, Expr (:call , op. func, new_ops... ))
443457 push! (driver. worklist, matched_ssa)
444458 return matched_ssa
445459end
@@ -454,25 +468,22 @@ The matched_ssas in MatchResult are ordered root-first, but we need to find
454468the specific SSA for a sub-pattern. We do this by looking up the first operand's
455469binding and finding the op that defines it."""
456470function find_matched_ssa (driver, pat:: PCall , bindings)
457- entry = driver. sci. entry
458471 for sub in pat. operands
459472 if sub isa PBind
460473 bound = get (bindings, sub. name, nothing )
461474 bound isa SSAValue || continue
462- for inst in users (entry, bound)
463- call = resolve_call (entry, inst)
464- call === nothing && continue
465- func, _ = call
466- func === pat. func && return SSAValue (inst)
475+ for u in users_of (driver, bound)
476+ def_entry = get (driver. defs, u, nothing )
477+ def_entry === nothing && continue
478+ def_entry. func === pat. func && return u
467479 end
468480 elseif sub isa PCall
469481 inner_ssa = find_matched_ssa (driver, sub, bindings)
470482 if inner_ssa != = nothing
471- for inst in users (entry, inner_ssa)
472- call = resolve_call (entry, inst)
473- call === nothing && continue
474- func, _ = call
475- func === pat. func && return SSAValue (inst)
483+ for u in users_of (driver, inner_ssa)
484+ def_entry = get (driver. defs, u, nothing )
485+ def_entry === nothing && continue
486+ def_entry. func === pat. func && return u
476487 end
477488 end
478489 end
@@ -491,20 +502,21 @@ function apply_rewrite!(driver::RewriteDriver, block, val::SSAValue, rule, match
491502 # Look up live instruction for RFunc interface
492503 haskey (block, val. id) || return false
493504 inst = block[val. id]
494- rule. rhs. func (driver. sci, block, inst, match, driver) || return false
505+ rule. rhs. func (driver. rewriter . sci, block, inst, match, driver) || return false
495506 return true
496507 elseif rule. rhs isa RBind
497508 # Forwarding: replace all uses of root with the bound value, delete root.
498509 # Mark immediate users as modified — their operands are about to change.
499510 # When these are later popped from the worklist without a match, the
500511 # driver propagates to THEIR users (see modified check in main loop).
501512 # This gives MLIR-style notifyOperationModified cascading.
502- for inst in users (driver. sci. entry, val)
503- push! (driver. modified, SSAValue (inst))
513+ new_val = match. bindings[rule. rhs. name]
514+ for u in users_of (driver, val)
515+ push! (driver. modified, u)
516+ push! (driver. worklist, u)
504517 end
505- add_users_to_worklist! (driver, val)
506- replace_uses! (driver. sci. entry, val, match. bindings[rule. rhs. name])
507- erase_op! (driver, entry)
518+ replace_uses! (driver. rewriter, val, new_val)
519+ erase! (driver. rewriter, entry. block, val)
508520 else
509521 # Substitution: replace root in-place, clean up dead intermediates.
510522 # Only delete intermediates with no remaining uses — transparent-op
@@ -514,7 +526,7 @@ function apply_rewrite!(driver::RewriteDriver, block, val::SSAValue, rule, match
514526 dead_entry = get (driver. defs, dead_val, nothing )
515527 dead_entry === nothing && continue
516528 use_count (driver, dead_val) == 0 || continue
517- erase_op ! (driver, dead_entry)
529+ erase ! (driver. rewriter , dead_entry. block, dead_val )
518530 end
519531 typ = block[val. id][:type ]
520532 # Build operands, flattening RSplat nodes into multiple operands
@@ -531,13 +543,8 @@ function apply_rewrite!(driver::RewriteDriver, block, val::SSAValue, rule, match
531543 # describe the OLD op; recompute from the new func's `efunc` effects
532544 # so downstream gates (CSE, LICM) see fresh, correct information.
533545 new_stmt = Expr (:call , rule. rhs. func, operands... )
534- if rule. rhs. func === driver. defs[val]. func
535- block[val. id] = (stmt= new_stmt,)
536- else
537- block[val. id] = (stmt= new_stmt, flag= inferred_flags (rule. rhs. func))
538- end
539- # Update defs, re-add self and users to worklist (statement changed)
540- driver. defs[val] = DefEntry (block, val, rule. rhs. func)
546+ flag = rule. rhs. func === driver. defs[val]. func ? nothing : inferred_flags (rule. rhs. func)
547+ replace_stmt! (driver. rewriter, block, val, new_stmt; flag)
541548 push! (driver. worklist, val)
542549 add_users_to_worklist! (driver, val)
543550 end
@@ -584,7 +591,10 @@ function rewrite_patterns!(sci::StructuredIRCode, rules::Vector{RewriteRule};
584591 end
585592 end
586593
587- driver = RewriteDriver (sci, defs, dispatch, wl, constants, Set {SSAValue} (), max_rewrites)
594+ rewriter = Rewriter (sci)
595+ driver = RewriteDriver (rewriter, defs, dispatch, wl, constants, Set {SSAValue} (),
596+ max_rewrites)
597+ rewriter. listener = driver
588598
589599 num_rewrites = 0
590600 while ! isempty (driver. worklist) && num_rewrites < driver. max_rewrites
@@ -605,7 +615,7 @@ function rewrite_patterns!(sci::StructuredIRCode, rules::Vector{RewriteRule};
605615 if use_count (driver, val) == 0
606616 stmt = entry. block[val. id][:stmt ]
607617 if ! must_keep (entry. block, stmt)
608- erase_op ! (driver, entry)
618+ erase ! (driver. rewriter , entry. block, val )
609619 continue
610620 end
611621 end
@@ -634,10 +644,9 @@ function rewrite_patterns!(sci::StructuredIRCode, rules::Vector{RewriteRule};
634644 # cascade continues through the fixpoint.
635645 if ! matched && val in driver. modified
636646 delete! (driver. modified, val)
637- for inst in users (driver. sci. entry, val)
638- uv = SSAValue (inst)
639- push! (driver. modified, uv)
640- haskey (driver. defs, uv) && push! (driver. worklist, uv)
647+ for u in users_of (driver, val)
648+ push! (driver. modified, u)
649+ haskey (driver. defs, u) && push! (driver. worklist, u)
641650 end
642651 end
643652 end
0 commit comments