[mlir] Use MLIR op names when generating FileCheck variables in generate-test-checks.py (#160820)
Motivation ---------- Improve readability and stability of autogenerated CHECK lines by using operation-aware FileCheck variable names instead of generic VAL_N. What changes ------------ - When possible, variable names are derived from the MLIR op name, e.g. `vector.transfer_read` → `TRANSFER_READ_0`. - Unknown ops (e.g., from out-of-tree dialects) fall back to the prior `VAL_N` scheme. Before ------ ```mlir // CHECK: %[[VAL_4:.*]] = vector.transfer_read ... // CHECK: %[[VAL_5:.*]] = "val_use"(%[[VAL_4]]) : ... ``` After ----- ```mlir // CHECK: %[[TRANSFER_READ_0:.*]] = vector.transfer_read ... // CHECK: %[[VAL_1:.*]] = "val_use"(%[[TRANSFER_READ_0]]) : ... ``` Rationale --------- Using op-derived names (e.g., `TRANSFER_READ_0`) makes tests easier to read and audit, while remaining more stable across unrelated edits (e.g. there will always be fewer `TRANSFER_READ_#N` variables than `VAL_#N`). The fallback to `VAL_N` preserves compatibility for unknown ops.
This commit is contained in:
parent
24c5926bd6
commit
b228a18e57
@ -31,13 +31,16 @@ import argparse
|
||||
import os # Used to advertise this file's name ("autogenerated_note").
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
ADVERT_BEGIN = "// NOTE: Assertions have been autogenerated by "
|
||||
ADVERT_END = """
|
||||
// The script is designed to make adding checks to
|
||||
// a test case fast, it is *not* designed to be authoritative
|
||||
// about what constitutes a good test! The CHECK should be
|
||||
// minimized and named to reflect the test intent.
|
||||
// This script is intended to make adding checks to a test case quick and easy.
|
||||
// It is *not* authoritative about what constitutes a good test. After using the
|
||||
// script, be sure to review and refine the generated checks. For example,
|
||||
// CHECK lines should be minimized and named to reflect the test’s intent.
|
||||
// For comprehensive guidelines, see:
|
||||
// * https://mlir.llvm.org/getting_started/TestingGuide/
|
||||
"""
|
||||
|
||||
|
||||
@ -45,6 +48,9 @@ ADVERT_END = """
|
||||
SSA_RE_STR = "[0-9]+|[a-zA-Z$._-][a-zA-Z0-9$._-]*"
|
||||
SSA_RE = re.compile(SSA_RE_STR)
|
||||
|
||||
# Regex matching `dialect.op_name` (e.g. `vector.transfer_read`).
|
||||
SSA_OP_NAME_RE = re.compile(r"\b(?:\s=\s[a-z_]+)[.]([a-z_]+)\b")
|
||||
|
||||
# Regex matching the left-hand side of an assignment
|
||||
SSA_RESULTS_STR = r'\s*(%' + SSA_RE_STR + r')(\s*,\s*(%' + SSA_RE_STR + r'))*\s*='
|
||||
SSA_RESULTS_RE = re.compile(SSA_RESULTS_STR)
|
||||
@ -63,7 +69,12 @@ ATTR_DEF_RE = re.compile(ATTR_DEF_RE_STR)
|
||||
class VariableNamer:
|
||||
def __init__(self, variable_names):
|
||||
self.scopes = []
|
||||
# Counter for generic FileCHeck names, e.g. VAL_#N
|
||||
self.name_counter = 0
|
||||
# Counters for FileCheck names derived from Op names, e.g.
|
||||
# TRANSFER_READ_#N (based on `vector.transfer_read`). Note, there's a
|
||||
# dedicated counter for every Op type present in the input.
|
||||
self.op_name_counter = Counter()
|
||||
|
||||
# Number of variable names to still generate in parent scope
|
||||
self.generate_in_parent_scope_left = 0
|
||||
@ -77,17 +88,29 @@ class VariableNamer:
|
||||
self.generate_in_parent_scope_left = n
|
||||
|
||||
# Generate a substitution name for the given ssa value name.
|
||||
def generate_name(self, source_variable_name, use_ssa_name):
|
||||
def generate_name(self, source_variable_name, use_ssa_name, op_name=""):
|
||||
|
||||
# Compute variable name
|
||||
variable_name = self.variable_names.pop(0) if len(self.variable_names) > 0 else ''
|
||||
if variable_name == '':
|
||||
variable_name = (
|
||||
self.variable_names.pop(0) if len(self.variable_names) > 0 else ""
|
||||
)
|
||||
if variable_name == "":
|
||||
# If `use_ssa_name` is set, use the MLIR SSA value name to generate
|
||||
# a FileCHeck substation string. As FileCheck requires these
|
||||
# strings to start with a character, skip MLIR variables starting
|
||||
# with a digit (e.g. `%0`).
|
||||
#
|
||||
# The next fallback option is to use the op name, if the
|
||||
# corresponding match succeeds.
|
||||
#
|
||||
# If neither worked, use a generic name: `VAL_#N`.
|
||||
if use_ssa_name and source_variable_name[0].isalpha():
|
||||
variable_name = source_variable_name.upper()
|
||||
elif op_name != "":
|
||||
variable_name = (
|
||||
op_name.upper() + "_" + str(self.op_name_counter[op_name])
|
||||
)
|
||||
self.op_name_counter[op_name] += 1
|
||||
else:
|
||||
variable_name = "VAL_" + str(self.name_counter)
|
||||
self.name_counter += 1
|
||||
@ -123,6 +146,7 @@ class VariableNamer:
|
||||
def clear_names(self):
|
||||
self.name_counter = 0
|
||||
self.used_variable_names = set()
|
||||
self.op_name_counter.clear()
|
||||
|
||||
class AttributeNamer:
|
||||
|
||||
@ -170,8 +194,12 @@ def process_line(line_chunks, variable_namer, use_ssa_name=False, strict_name_re
|
||||
|
||||
# Process the rest that contained an SSA value name.
|
||||
for chunk in line_chunks:
|
||||
m = SSA_RE.match(chunk)
|
||||
ssa_name = m.group(0) if m is not None else ''
|
||||
ssa = SSA_RE.match(chunk)
|
||||
op_name_with_dialect = SSA_OP_NAME_RE.search(chunk)
|
||||
ssa_name = ssa.group(0) if ssa is not None else ""
|
||||
op_name = (
|
||||
op_name_with_dialect.group(1) if op_name_with_dialect is not None else ""
|
||||
)
|
||||
|
||||
# Check if an existing variable exists for this name.
|
||||
variable = None
|
||||
@ -185,7 +213,7 @@ def process_line(line_chunks, variable_namer, use_ssa_name=False, strict_name_re
|
||||
output_line += "%[[" + variable + "]]"
|
||||
else:
|
||||
# Otherwise, generate a new variable.
|
||||
variable = variable_namer.generate_name(ssa_name, use_ssa_name)
|
||||
variable = variable_namer.generate_name(ssa_name, use_ssa_name, op_name)
|
||||
if strict_name_re:
|
||||
# Use stricter regexp for the variable name, if requested.
|
||||
# Greedy matching may cause issues with the generic '.*'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user