Skip to content

Commit 992462a

Browse files
committed
update_test_checks: improve IR value name stability
By default, UTC attempts to keep the produced diff small by keeping IR value name variables stable. The old algorithm was roughly: 1. Compute a diff between the old and new check lines, where "uncommitted" variable names are replaced by a wildcard. This leads to a set of non-crossing "candidate" pairs of (old line, new line) that we can try to make equal. 2. Greedily walk this list of candidates, committing to variable names that make candidate lines equal if possible. The greedy approach in the second step has the downside that committing to a variable name greedily can sometimes prevent many subsequent candidates from getting the variable name assignment that would make them equal. We keep the first step as-is, but replace the second one by an algorithm that finds a large independent set of candidates, i.e. candidate pairs of (old line, new line) which are non-conflicting in the sense that their desired variable name mappings are not in conflict. We find the large independent set by greedily assigning a coloring to the conflict graph and taking the largest color class. We then commit to all the variable name mappings which are desired by candidates in this largest color class. As before, we then recurse into regions between matching lines. This is required in large cases. For example, running this algorithm at the top-level of the new test case (stable_ir_values5.ll) matches up most of the instructions, but not the names of the result values of all the `load`s. This is because (unlike e.g. the getelementptrs) the load instructions are all equal except for variable names, and so step 1 (the diff algorithm) doesn't consider them as candidates. However, they are trivially matched by recursion. As is usually the case with these changes, the quality improvement is hard to see from the diff of this patch. However, it becomes obvious when comparing the diff of stable_ir_values5.ll against stable_ir_value5.ll.expected before and after this change.
1 parent d87de5e commit 992462a

File tree

2 files changed

+102
-58
lines changed

2 files changed

+102
-58
lines changed

llvm/test/tools/UpdateTestChecks/update_test_checks/Inputs/stable_ir_values5.ll.expected

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ define dso_local void @main.resume.0(i64 %0, %structA %arg01, [23 x i32] %arg12,
1414
; CHECK-SAME: i64 [[TMP0:%.*]], [[STRUCTA:%.*]] [[ARG01:%.*]], [23 x i32] [[ARG12:%.*]], [30 x i32] [[ARG23:%.*]]) {
1515
; CHECK-NEXT: entryresume.0:
1616
; CHECK-NEXT: [[FOO:%.*]] = call ptr @getter(i32 108)
17-
; CHECK-NEXT: [[TMP5:%.*]] = insertvalue { [[STRUCTA]], [23 x i32], [30 x i32] } poison, [[STRUCTA]] [[ARG01]], 0
18-
; CHECK-NEXT: [[TMP6:%.*]] = insertvalue { [[STRUCTA]], [23 x i32], [30 x i32] } [[TMP5]], [23 x i32] [[ARG12]], 1
19-
; CHECK-NEXT: [[TMP1:%.*]] = insertvalue { [[STRUCTA]], [23 x i32], [30 x i32] } [[TMP6]], [30 x i32] [[ARG23]], 2
17+
; CHECK-NEXT: [[TMP6:%.*]] = insertvalue { [[STRUCTA]], [23 x i32], [30 x i32] } poison, [[STRUCTA]] [[ARG01]], 0
18+
; CHECK-NEXT: [[TMP8:%.*]] = insertvalue { [[STRUCTA]], [23 x i32], [30 x i32] } [[TMP6]], [23 x i32] [[ARG12]], 1
19+
; CHECK-NEXT: [[TMP1:%.*]] = insertvalue { [[STRUCTA]], [23 x i32], [30 x i32] } [[TMP8]], [30 x i32] [[ARG23]], 2
2020
; CHECK-NEXT: [[TMP3:%.*]] = extractvalue { [[STRUCTA]], [23 x i32], [30 x i32] } [[TMP1]], 2
2121
; CHECK-NEXT: [[DOTFCA_0_EXTRACT:%.*]] = extractvalue [30 x i32] [[TMP3]], 0
2222
; CHECK-NEXT: [[DOTFCA_1_EXTRACT:%.*]] = extractvalue [30 x i32] [[TMP3]], 1
@@ -100,59 +100,59 @@ define dso_local void @main.resume.0(i64 %0, %structA %arg01, [23 x i32] %arg12,
100100
; CHECK-NEXT: [[DOTFCA_0_48_EXTRACT:%.*]] = extractvalue [[STRUCTC]] [[TMP4]], 0, 48
101101
; CHECK-NEXT: [[DOTFCA_0_49_EXTRACT:%.*]] = extractvalue [[STRUCTC]] [[TMP4]], 0, 49
102102
; CHECK-NEXT: [[TMP2:%.*]] = inttoptr i32 [[DOTFCA_0_EXTRACT]] to ptr
103-
; CHECK-NEXT: [[TMP8:%.*]] = load i32, ptr [[TMP2]], align 4
103+
; CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP2]], align 4
104104
; CHECK-NEXT: [[TMP27:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 1
105-
; CHECK-NEXT: [[TMP10:%.*]] = load i32, ptr [[TMP27]], align 4
105+
; CHECK-NEXT: [[TMP7:%.*]] = load i32, ptr [[TMP27]], align 4
106106
; CHECK-NEXT: [[TMP29:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 2
107-
; CHECK-NEXT: [[TMP12:%.*]] = load i32, ptr [[TMP29]], align 4
107+
; CHECK-NEXT: [[TMP9:%.*]] = load i32, ptr [[TMP29]], align 4
108108
; CHECK-NEXT: [[TMP31:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 3
109-
; CHECK-NEXT: [[TMP14:%.*]] = load i32, ptr [[TMP31]], align 4
109+
; CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr [[TMP31]], align 4
110110
; CHECK-NEXT: [[TMP33:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 4
111-
; CHECK-NEXT: [[TMP16:%.*]] = load i32, ptr [[TMP33]], align 4
111+
; CHECK-NEXT: [[TMP13:%.*]] = load i32, ptr [[TMP33]], align 4
112112
; CHECK-NEXT: [[TMP35:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 5
113-
; CHECK-NEXT: [[TMP18:%.*]] = load i32, ptr [[TMP35]], align 4
113+
; CHECK-NEXT: [[TMP15:%.*]] = load i32, ptr [[TMP35]], align 4
114114
; CHECK-NEXT: [[TMP37:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 6
115-
; CHECK-NEXT: [[TMP20:%.*]] = load i32, ptr [[TMP37]], align 4
115+
; CHECK-NEXT: [[TMP17:%.*]] = load i32, ptr [[TMP37]], align 4
116116
; CHECK-NEXT: [[TMP39:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 7
117-
; CHECK-NEXT: [[TMP22:%.*]] = load i32, ptr [[TMP39]], align 4
117+
; CHECK-NEXT: [[TMP19:%.*]] = load i32, ptr [[TMP39]], align 4
118118
; CHECK-NEXT: [[TMP41:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 8
119-
; CHECK-NEXT: [[TMP24:%.*]] = load i32, ptr [[TMP41]], align 4
119+
; CHECK-NEXT: [[TMP21:%.*]] = load i32, ptr [[TMP41]], align 4
120120
; CHECK-NEXT: [[TMP43:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 9
121-
; CHECK-NEXT: [[TMP26:%.*]] = load i32, ptr [[TMP43]], align 4
121+
; CHECK-NEXT: [[TMP24:%.*]] = load i32, ptr [[TMP43]], align 4
122122
; CHECK-NEXT: [[TMP45:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 10
123-
; CHECK-NEXT: [[TMP28:%.*]] = load i32, ptr [[TMP45]], align 4
123+
; CHECK-NEXT: [[TMP25:%.*]] = load i32, ptr [[TMP45]], align 4
124124
; CHECK-NEXT: [[TMP47:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 11
125-
; CHECK-NEXT: [[TMP30:%.*]] = load i32, ptr [[TMP47]], align 4
125+
; CHECK-NEXT: [[TMP28:%.*]] = load i32, ptr [[TMP47]], align 4
126126
; CHECK-NEXT: [[TMP49:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 12
127-
; CHECK-NEXT: [[TMP32:%.*]] = load i32, ptr [[TMP49]], align 4
127+
; CHECK-NEXT: [[TMP30:%.*]] = load i32, ptr [[TMP49]], align 4
128128
; CHECK-NEXT: [[TMP51:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 13
129-
; CHECK-NEXT: [[TMP34:%.*]] = load i32, ptr [[TMP51]], align 4
129+
; CHECK-NEXT: [[TMP32:%.*]] = load i32, ptr [[TMP51]], align 4
130130
; CHECK-NEXT: [[TMP53:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 14
131-
; CHECK-NEXT: [[TMP36:%.*]] = load i32, ptr [[TMP53]], align 4
131+
; CHECK-NEXT: [[TMP34:%.*]] = load i32, ptr [[TMP53]], align 4
132132
; CHECK-NEXT: [[TMP55:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 15
133-
; CHECK-NEXT: [[TMP38:%.*]] = load i32, ptr [[TMP55]], align 4
133+
; CHECK-NEXT: [[TMP36:%.*]] = load i32, ptr [[TMP55]], align 4
134134
; CHECK-NEXT: [[TMP57:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 16
135-
; CHECK-NEXT: [[TMP40:%.*]] = load i32, ptr [[TMP57]], align 4
135+
; CHECK-NEXT: [[TMP38:%.*]] = load i32, ptr [[TMP57]], align 4
136136
; CHECK-NEXT: [[TMP59:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 17
137-
; CHECK-NEXT: [[TMP42:%.*]] = load i32, ptr [[TMP59]], align 4
137+
; CHECK-NEXT: [[TMP40:%.*]] = load i32, ptr [[TMP59]], align 4
138138
; CHECK-NEXT: [[TMP61:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 18
139-
; CHECK-NEXT: [[TMP44:%.*]] = load i32, ptr [[TMP61]], align 4
139+
; CHECK-NEXT: [[TMP42:%.*]] = load i32, ptr [[TMP61]], align 4
140140
; CHECK-NEXT: [[TMP63:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 19
141-
; CHECK-NEXT: [[TMP46:%.*]] = load i32, ptr [[TMP63]], align 4
141+
; CHECK-NEXT: [[TMP44:%.*]] = load i32, ptr [[TMP63]], align 4
142142
; CHECK-NEXT: [[TMP65:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 20
143-
; CHECK-NEXT: [[TMP48:%.*]] = load i32, ptr [[TMP65]], align 4
143+
; CHECK-NEXT: [[TMP46:%.*]] = load i32, ptr [[TMP65]], align 4
144144
; CHECK-NEXT: [[TMP67:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 21
145-
; CHECK-NEXT: [[TMP50:%.*]] = load i32, ptr [[TMP67]], align 4
145+
; CHECK-NEXT: [[TMP48:%.*]] = load i32, ptr [[TMP67]], align 4
146146
; CHECK-NEXT: [[TMP69:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 22
147-
; CHECK-NEXT: [[TMP52:%.*]] = load i32, ptr [[TMP69]], align 4
147+
; CHECK-NEXT: [[TMP50:%.*]] = load i32, ptr [[TMP69]], align 4
148148
; CHECK-NEXT: [[TMP71:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 23
149-
; CHECK-NEXT: [[TMP54:%.*]] = load i32, ptr [[TMP71]], align 4
149+
; CHECK-NEXT: [[TMP52:%.*]] = load i32, ptr [[TMP71]], align 4
150150
; CHECK-NEXT: [[TMP73:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 24
151-
; CHECK-NEXT: [[TMP56:%.*]] = load i32, ptr [[TMP73]], align 4
151+
; CHECK-NEXT: [[TMP54:%.*]] = load i32, ptr [[TMP73]], align 4
152152
; CHECK-NEXT: [[TMP75:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 25
153-
; CHECK-NEXT: [[TMP62:%.*]] = load i32, ptr [[TMP75]], align 4
153+
; CHECK-NEXT: [[TMP56:%.*]] = load i32, ptr [[TMP75]], align 4
154154
; CHECK-NEXT: [[TMP77:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i32 26
155-
; CHECK-NEXT: [[TMP64:%.*]] = load i32, ptr [[TMP77]], align 4
155+
; CHECK-NEXT: [[TMP62:%.*]] = load i32, ptr [[TMP77]], align 4
156156
; CHECK-NEXT: [[TMP60:%.*]] = inttoptr i32 [[DOTFCA_0_EXTRACT]] to ptr
157157
; CHECK-NEXT: [[TMP58:%.*]] = extractvalue { [[STRUCTA]], [23 x i32], [30 x i32] } [[TMP1]], 0
158158
; CHECK-NEXT: [[DOTFCA_0_EXTRACT57:%.*]] = extractvalue [[STRUCTA]] [[TMP58]], 0

llvm/utils/UpdateTestChecks/common.py

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,14 +1534,35 @@ def diffify_line(line, mapper):
15341534

15351535
candidate_matches = find_diff_matching(lhs_lines, rhs_lines)
15361536

1537-
# Apply commits greedily on a match-by-match basis
1538-
matches = [(-1, -1)]
1539-
committed_anything = False
1540-
for lhs_idx, rhs_idx in candidate_matches:
1537+
candidate_matches = [
1538+
(old_begin + lhs_idx, new_begin + rhs_idx)
1539+
for lhs_idx, rhs_idx in candidate_matches
1540+
]
1541+
1542+
# Candidate matches may conflict if they require conflicting mappings of
1543+
# names.
1544+
#
1545+
# Treat the candidate matches as vertices in a conflict graph. Greedily
1546+
# color the vertices.
1547+
class Color:
1548+
def __init__(self):
1549+
# (lhs_idx, rhs_idx) of matches in this color
1550+
self.matches = []
1551+
1552+
# rhs_name -> lhs_name mappings required by this color
1553+
self.mapping = {}
1554+
1555+
# lhs_names committed for this color
1556+
self.committed = set()
1557+
colors = []
1558+
1559+
for match_idx, (lhs_idx, rhs_idx) in enumerate(candidate_matches):
15411560
lhs_line = old_line_infos[lhs_idx]
15421561
rhs_line = new_line_infos[rhs_idx]
15431562

1544-
local_commits = {}
1563+
compatible_colors = colors[:]
1564+
new_color = Color()
1565+
new_color.matches.append((lhs_idx, rhs_idx))
15451566

15461567
for lhs_value, rhs_value in zip(lhs_line.values, rhs_line.values):
15471568
if new_mapping[rhs_value.name] in committed_names:
@@ -1554,39 +1575,62 @@ def diffify_line(line, mapper):
15541575
else:
15551576
break
15561577

1557-
if rhs_value.name in local_commits:
1578+
if rhs_value.name in new_color.mapping:
15581579
# Same, but for a possible commit happening on the same line
1559-
if local_commits[rhs_value.name] == lhs_value.name:
1580+
if new_color.color[rhs_value.name] == lhs_value.name:
15601581
continue
15611582
else:
15621583
break
15631584

1564-
if lhs_value.name in committed_names:
1565-
# We can't map this value because the name we would map it to has already been
1566-
# committed for something else. Give up on this line.
1585+
if lhs_value.name in committed_names or lhs_value.name in new_color.committed:
1586+
# We can't map this value because the name we would map it
1587+
# to has already been committed for something else. Give up
1588+
# on this line.
15671589
break
15681590

1569-
local_commits[rhs_value.name] = lhs_value.name
1570-
else:
1571-
# No reason not to add any commitments for this line
1572-
for rhs_var, lhs_var in local_commits.items():
1573-
new_mapping[rhs_var] = lhs_var
1574-
committed_names.add(lhs_var)
1575-
committed_anything = True
1576-
1577-
if (
1578-
lhs_var != rhs_var
1579-
and lhs_var in new_mapping
1580-
and new_mapping[lhs_var] == lhs_var
1581-
):
1582-
new_mapping[lhs_var] = "conflict_" + lhs_var
1591+
new_color.mapping[rhs_value.name] = lhs_value.name
1592+
new_color.committed.add(lhs_value.name)
15831593

1584-
matches.append((lhs_idx, rhs_idx))
1594+
color_idx = 0
1595+
while color_idx < len(compatible_colors):
1596+
color = compatible_colors[color_idx]
1597+
compatible = True
1598+
if rhs_value.name in color.mapping:
1599+
compatible = color.mapping[rhs_value.name] == lhs_value.name
1600+
else:
1601+
compatible = lhs_value.name not in color.committed
1602+
if compatible:
1603+
color_idx += 1
1604+
else:
1605+
del compatible_colors[color_idx]
1606+
else:
1607+
# At a minimum, this line is viable standalone
1608+
if compatible_colors:
1609+
compatible_colors[0].mapping.update(new_color.mapping)
1610+
compatible_colors[0].committed.update(new_color.committed)
1611+
compatible_colors[0].matches.append((lhs_idx, rhs_idx))
1612+
else:
1613+
colors.append(new_color)
1614+
1615+
if colors:
1616+
# Pick the largest color class. This gives us a large independent
1617+
# (non-conflicting) set of candidate matches. Assign all names
1618+
# required by the independent set and recurse.
1619+
max_color = max(colors, key=lambda color: len(color.matches))
1620+
1621+
for rhs_var, lhs_var in max_color.mapping.items():
1622+
new_mapping[rhs_var] = lhs_var
1623+
committed_names.add(lhs_var)
1624+
1625+
if (
1626+
lhs_var != rhs_var
1627+
and lhs_var in new_mapping
1628+
and new_mapping[lhs_var] == lhs_var
1629+
):
1630+
new_mapping[lhs_var] = "conflict_" + lhs_var
15851631

1586-
matches.append((old_end, new_end))
1632+
matches = [(old_begin - 1, new_begin - 1)] + max_color.matches + [(old_end, new_end)]
15871633

1588-
# Recursively handle sequences between matches
1589-
if committed_anything:
15901634
for (lhs_prev, rhs_prev), (lhs_next, rhs_next) in zip(matches, matches[1:]):
15911635
recurse(lhs_prev + 1, lhs_next, rhs_prev + 1, rhs_next)
15921636

0 commit comments

Comments
 (0)