Module Tolk_uop.Upat
Pattern DSL for matching and rewriting Uop nodes.
A Upat.t describes a structural shape over the uop DAG. Patterns are built with smart constructors and infix operators, paired with a callback through (=>), and collected into a Pattern_matcher.t that applies the first matching rule whose callback returns Some _.
Patterns constrain any subset of a node's op, dtype, src, and arg. They can also constrain Uop.node_tag through tag. A pattern with no constraints is a wildcard. Named patterns capture the matched node into a Bindings access table; the same name used twice in one pattern requires the two captures to be physically equal.
The module is designed to be locally opened — `Upat.(...)` or `let open Upat in ...` — which brings pattern constructors, operators, and => all into scope at once.
Example
let open Upat in
Pattern_matcher.make [
(* x + 0 -> x *)
var "x" + zero => (fun b -> Some (b $ "x"));
(* x * 1 -> x *)
rewrite1 (fun x -> x * one) (fun x -> Some x);
]Types
The type for dtype constraints. Constructors that accept ?dtype:Dtype.t follow tinygrad matching: exact dtype or matching scalar identity. Use exact_dtype when vector width and pointer metadata must also match.
The type for constraints on a uop's Arg field. Build with arg_any, arg_eq, has_int, has_const, has_op, has_op_in, has_reduce_op, or has_reduce_op_in.
The table of name-to-node captures produced by a successful match. A name appearing multiple times in a pattern binds to the same node in all positions.
The type for a pattern paired with a context-sensitive rewrite callback. The context is supplied by Pattern_matcher.rewrite_with_ctx.
Pattern constructors
val any : tany is the wildcard: matches any node and captures nothing.
val var : string -> tvar name is a wildcard that captures the matched node under name. If name already appears earlier in the pattern, the two matched nodes must be equal (via Uop.equal) for the match to succeed.
Raises Invalid_argument if name is "ctx", which is reserved for rewrite callback context.
exact_dtype dt matches exactly dt, including vector width and pointer metadata.
val scalar_dtype : Dtype.scalar -> dtype_patscalar_dtype s matches any dtype whose scalar identity is s, regardless of vector width.
any_dtype ps matches when any dtype pattern in ps matches.
prefix ps matches ps at the beginning of the source list and accepts any trailing sources.
perms ps matches the source list as any permutation of ps. Duplicate binding sets are collapsed.
var_dtype name dt is var restricted by the explicit dtype pattern dt.
val var_scalar : string -> Dtype.scalar -> tvar_scalar name s is var_dtype name (scalar_dtype s).
val op :
?loc:pos ->
?dtype:Dtype.t ->
?src:t list ->
?arg:arg_pat ->
?name:string ->
?allow_any_len:bool ->
Ops.t ->
top ?dtype ?src ?arg ?name ?allow_any_len o matches a node whose op is o.
src is a positional match: each pattern in the list is applied to the child at the same index. By default the pattern requires the exact same number of children as the list length; with ~allow_any_len:true extra trailing children are accepted (but not fewer). Omitting src imposes no constraint on children.
arg defaults to arg_any. Use arg_eq, has_int, has_const, has_op, has_op_in, has_reduce_op, or has_reduce_op_in to constrain the payload.
dtype pins the node's dtype when given.
name captures the matching node under that key, with the uniqueness rule of var.
This constructor does not canonicalise commutative operands; use alu for that.
val ops :
?loc:pos ->
?dtype:Dtype.t ->
?src:t list ->
?arg:arg_pat ->
?name:string ->
?allow_any_len:bool ->
Ops.t list ->
tops os is like op but matches any node whose op is in os.
val op_src :
?loc:pos ->
?dtype:dtype_pat ->
?src:src_pat ->
?arg:arg_pat ->
?name:string ->
Ops.t ->
top_src is the explicit-source-pattern variant of op. Use fixed, prefix, perms, or repeat to describe child matching, and exact_dtype, scalar_dtype, or any_dtype for dtype matching.
val ops_src :
?loc:pos ->
?dtype:dtype_pat ->
?src:src_pat ->
?arg:arg_pat ->
?name:string ->
Ops.t list ->
tops_src is like op_src but matches any node whose op is in the supplied list.
const c matches an Ops.t.Const node whose value equals c.
val const_int : int -> tconst_int n matches a Ops.t.Const of value n with dtype Dtype.Val.weakint.
val const_float : float -> tconst_float x matches a Ops.t.Const of value x with dtype Dtype.Val.float32.
val const_bool : bool -> tconst_bool b matches a boolean Ops.t.Const of value b.
cvar ?arg () matches an Ops.t.Const, optionally restricted to arg.
tag s p is p restricted to nodes whose Uop.node_tag is Some s.
tags ss p is p restricted to nodes whose Uop.node_tag is one of ss. An empty ss never matches a tagged node.
early_reject ops p records a custom source-op precondition for p. A matcher can reject p before structural matching unless every op in ops appears among the candidate node's immediate sources.
located ~file ~line p is p annotated with source-location metadata for diagnostics. The annotation does not affect matching.
located_pos ~loc p is p annotated with the file and line in loc. Pass ~loc:__POS__ at rule construction sites for diagnostic locations.
val location : t -> (string * int) optionlocation p is p's diagnostic location, if any.
Constant literals
Shorthands for the most common constants in rewrite rules.
val zero : tzero is const_int 0.
val one : tone is const_int 1.
val neg_one : tneg_one is const_int (-1).
val true_ : ttrue_ is const_bool true.
val false_ : tfalse_ is const_bool false.
Chainable builders
Mirror the per-op src contracts of Uop. Each takes the principal child last so that pipelines read left-to-right.
load ?alt ?gate idx matches Ops.t.Load with src = (idx,), or src = (idx, alt, gate) when alt and gate are given.
Raises Invalid_argument if only one of alt or gate is supplied.
store ?gate dst value matches Ops.t.Store with src = (dst, value) or src = (dst, value, gate).
index idx ptr matches scalar Ops.t.Index with src = (ptr, idx). Use a raw pattern with allow_any_len to match variadic indexes.
cast p matches Ops.t.Cast of the child p, optionally pinning the result dtype.
bitcast p matches Ops.t.Bitcast of the child p, optionally pinning the result dtype.
gep ?idx p matches value-lane Ops.t.Index on p. When idx is given the index child is constrained to the weak-int constant idx; without idx any constant index is accepted.
sink srcs matches Ops.t.Sink whose first children start with srcs. Extra trailing children are accepted.
where cond then_ else_ matches Ops.t.Where with src = (cond, then_, else_).
alu args o matches an ALU node with op o and operands args. When o is binary and commutative (see Ops.Group.is_commutative), the operand order is canonicalised: the pattern succeeds on either permutation of args. Otherwise the match is positional.
Arg patterns
val arg_any : arg_patarg_any accepts any arg payload.
val has_int : int -> arg_pathas_int n matches Uop.Arg.Int n.
has_reduce_op_in os matches Uop.Arg.Reduce_arg { op; _ } when op is one of os.
Operators
Infix shorthands for alu-built binary patterns. Kept in a sub-module so that let open Upat in ... does not shadow int arithmetic inside rule bodies. Open locally with O.(...) for pattern expressions:
let rule =
let open Upat in
O.(var "x" + zero) => fun b -> Some (b $ "x")module O : sig ... endBindings access
b $ name is the node captured under name.
Raises Not_found if name is not bound.
find b name is Some u when name is bound to u, None otherwise.
val mem : bindings -> string -> boolmem b name is true iff name is bound.
val pp_bindings : Stdlib.Format.formatter -> bindings -> unitpp_bindings ppf b formats b as {name0=%tag0, name1=%tag1, ...} for debugging.
Matching
match_ p u is the list of bindings under which p matches u. Empty means no match; commutative and Perms-style patterns may produce more than one successful binding set, but duplicate binding sets are collapsed. Matching is purely local: children of u are inspected only as far as p requires.
Pattern matcher
pat => f is a rule pairing pat with callback f. The callback is invoked with each capture-binding set produced by a match; it returns Some u' to rewrite to u', or None to reject the match and let the matcher try the next binding or rule.
val with_ctx : t -> ('ctx -> bindings -> Uop.t option) -> 'ctx rule_with_ctxwith_ctx pat f is a context-sensitive rule. The callback receives the context passed to Pattern_matcher.rewrite_with_ctx.
Variadic capture combinators
Shortcut rule builders when the only captures are positional var wildcards. The pattern-builder and callback both take the captures as direct arguments, eliminating name strings and the bindings lookup.
rewrite1 p k builds the pattern by applying p to a single fresh var, and invokes k with the matched node. Using the same argument twice in p imposes an equality constraint (as with var).
Two-capture variant of rewrite1.
Three-capture variant of rewrite1.
val rewrite4 :
(t -> t -> t -> t -> t) ->
(Uop.t -> Uop.t -> Uop.t -> Uop.t -> Uop.t option) ->
ruleFour-capture variant of rewrite1.
module Pattern_matcher : sig ... end