1919
2020namespace patchestry ::passes {
2121
22- bool
23- OperationMatcher::matches (mlir::Operation *op, cir::FuncOp func, const PatchSpec &spec) {
22+ bool OperationMatcher::matches (
23+ mlir::Operation *op, cir::FuncOp func, const PatchSpec &spec,
24+ OperationMatcher::Mode mode // NOLINT
25+ ) {
2426 const auto &match = spec.match ;
2527
26- // If kind is not operation, or name is empty, return false
27- if (match.kind != MatchKind::OPERATION || match.name .empty ()) {
28+ // Handle different match kinds
29+ switch (mode) {
30+ case OperationMatcher::Mode::OPERATION :
31+ return matches_operation (op, func, match);
32+ case OperationMatcher::Mode::FUNCTION :
33+ return matches_function_call (op, func, match);
34+ }
35+
36+ return false ;
37+ }
38+
39+ bool OperationMatcher::matches_operation (
40+ mlir::Operation *op, cir::FuncOp func, const PatchMatch &match
41+ ) {
42+ // If the match kind is not operation, return false
43+ if (match.name .empty () || match.kind != MatchKind::OPERATION ) {
2844 return false ;
2945 }
3046
@@ -51,6 +67,46 @@ namespace patchestry::passes {
5167 return true ;
5268 }
5369
70+ bool OperationMatcher::matches_function_call (
71+ mlir::Operation *op, cir::FuncOp func, const PatchMatch &match
72+ ) {
73+ // If the match kind is not function, return false
74+ if (match.name .empty () || match.kind != MatchKind::FUNCTION ) {
75+ return false ;
76+ }
77+
78+ // For function-based matching, we expect a cir.call operation
79+ auto call_op = mlir::dyn_cast< cir::CallOp >(op);
80+ if (!call_op) {
81+ return false ;
82+ }
83+
84+ // Check if the called function name matches
85+ if (!match.name .empty ()) {
86+ std::string callee_name = extract_callee_name (call_op);
87+ if (!matches_pattern (callee_name, match.name )) {
88+ return false ;
89+ }
90+ }
91+
92+ // Check function context match (the function containing the call)
93+ if (!matches_function_context (func, match.function_context )) {
94+ return false ;
95+ }
96+
97+ // Check argument matches for function calls
98+ if (!matches_arguments (op, match.argument_matches )) {
99+ return false ;
100+ }
101+
102+ // Check variable matches as one of the arguments
103+ if (!matches_variables (op, match.variable_matches )) {
104+ return false ;
105+ }
106+
107+ return true ;
108+ }
109+
54110 bool OperationMatcher::matches_operation_name (
55111 mlir::Operation *op, const std::string &operation_pattern
56112 ) {
@@ -178,6 +234,92 @@ namespace patchestry::passes {
178234 return false ;
179235 }
180236
237+ bool OperationMatcher::matches_arguments (
238+ mlir::Operation *op, const std::vector< ArgumentMatch > &argument_matches
239+ ) {
240+ // If no argument matches specified, consider it a match
241+ if (argument_matches.empty ()) {
242+ return true ;
243+ }
244+
245+ auto operands = op->getOperands ();
246+
247+ for (const auto &arg_match : argument_matches) {
248+ // Check if the argument index is valid
249+ if (arg_match.index >= operands.size ()) {
250+ return false ;
251+ }
252+
253+ auto operand = operands[arg_match.index ];
254+
255+ // Check argument name if specified
256+ if (!arg_match.name .empty ()) {
257+ std::string var_name = extract_variable_name (op, arg_match.index );
258+ if (!matches_pattern (var_name, arg_match.name )) {
259+ return false ;
260+ }
261+ }
262+
263+ // Check argument type if specified
264+ if (!arg_match.type .empty ()) {
265+ if (!matches_type (operand.getType (), arg_match.type )) {
266+ return false ;
267+ }
268+ }
269+ }
270+
271+ return true ;
272+ }
273+
274+ bool OperationMatcher::matches_variables (
275+ mlir::Operation *op, const std::vector< VariableMatch > &variable_matches
276+ ) {
277+ // If no variable matches specified, consider it a match
278+ if (variable_matches.empty ()) {
279+ return true ;
280+ }
281+
282+ // Check operands for variable matches
283+ auto operands = op->getOperands ();
284+ for (unsigned i = 0 ; i < operands.size (); ++i) {
285+ auto operand = operands[i];
286+ std::string var_name = extract_variable_name (op, i);
287+ std::string var_type = type_to_string (operand.getType ());
288+
289+ for (const auto &var_match : variable_matches) {
290+ bool name_matches =
291+ var_match.name .empty () || matches_pattern (var_name, var_match.name );
292+ bool type_matches =
293+ var_match.type .empty () || matches_pattern (var_type, var_match.type );
294+
295+ if (name_matches && type_matches) {
296+ return true ;
297+ }
298+ }
299+ }
300+
301+ return false ;
302+ }
303+
304+ std::string OperationMatcher::extract_callee_name (cir::CallOp call_op) {
305+ // Extract the called function name from the call operation
306+ if (auto callee = call_op.getCalleeAttr ()) {
307+ return callee.getLeafReference ().str ();
308+ }
309+
310+ // If no direct callee attribute, try to extract from operands
311+ auto operands = call_op.getOperands ();
312+ if (!operands.empty ()) {
313+ auto first_operand = operands[0 ];
314+ std::string name = extract_ssa_value_name (first_operand);
315+ if (!name.empty ()) {
316+ return name;
317+ }
318+ }
319+
320+ return " " ;
321+ }
322+
181323 bool
182324 OperationMatcher::matches_pattern (const std::string &text, const std::string &pattern) {
183325 // if no pattern, match all
0 commit comments