@@ -72,6 +72,161 @@ PhaseStatus Compiler::optRedundantBranches()
72
72
return visitor.madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
73
73
}
74
74
75
+ static const ValueNumStore::VN_RELATION_KIND s_vnRelations[] = {ValueNumStore::VN_RELATION_KIND::VRK_Same,
76
+ ValueNumStore::VN_RELATION_KIND::VRK_Reverse,
77
+ ValueNumStore::VN_RELATION_KIND::VRK_Swap,
78
+ ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse};
79
+
80
+ // ------------------------------------------------------------------------
81
+ // RelopImplicationInfo
82
+ //
83
+ // Describes information needed to check for and describe the
84
+ // inferences between two relops.
85
+ //
86
+ struct RelopImplicationInfo
87
+ {
88
+ // Dominating relop, whose value may be determined by control flow
89
+ ValueNum domCmpNormVN = ValueNumStore::NoVN;
90
+ // Dominated relop, whose value we would like to determine
91
+ ValueNum treeNormVN = ValueNumStore::NoVN;
92
+ // Relationship between the two relops, if any
93
+ ValueNumStore::VN_RELATION_KIND vnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Same;
94
+ // Can we draw an inference?
95
+ bool canInfer = false ;
96
+ // If canInfer and ominating relop is true, can we infer value of dominated relop?
97
+ bool canInferFromTrue = true ;
98
+ // If canInfer and dominating relop is false, can we infer value of dominated relop?
99
+ bool canInferFromFalse = true ;
100
+ // Reverse the sense of the inference
101
+ bool reverseSense = false ;
102
+ };
103
+
104
+ // ------------------------------------------------------------------------
105
+ // optRedundantBranch: try and optimize a possibly redundant branch
106
+ //
107
+ // Arguments:
108
+ // rii - struct with relop implication information
109
+ //
110
+ // Returns:
111
+ // No return value.
112
+ // Sets rii->canInfer and other fields, if inference is possible.
113
+ //
114
+ // Notes:
115
+ //
116
+ // First looks for exact or similar relations.
117
+ //
118
+ // If that fails, then looks for cases where the user or optOptimizeBools
119
+ // has combined two distinct predicates with a boolean AND, OR, or has wrapped
120
+ // a predicate in NOT.
121
+ //
122
+ // This will be expressed as {NE/EQ}({AND/OR/NOT}(...), 0).
123
+ // If the operator is EQ then a true {AND/OR} result implies
124
+ // a false taken branch, so we need to invert the sense of our
125
+ // inferences.
126
+ //
127
+ // Note we could also infer the tree relop's value from other
128
+ // dominating relops, for example, (x >= 0) dominating (x > 0).
129
+ // That is left as a future enhancement.
130
+ //
131
+ void Compiler::optRelopImpliesRelop (RelopImplicationInfo* rii)
132
+ {
133
+ assert (!rii->canInfer );
134
+
135
+ // Look for related VNs
136
+ //
137
+ for (auto vnRelation : s_vnRelations)
138
+ {
139
+ const ValueNum relatedVN = vnStore->GetRelatedRelop (rii->domCmpNormVN , vnRelation);
140
+ if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == rii->treeNormVN ))
141
+ {
142
+ rii->canInfer = true ;
143
+ rii->vnRelation = vnRelation;
144
+ return ;
145
+ }
146
+ }
147
+
148
+ // VNs are not directly related. See if dominating
149
+ // compare encompasses a related VN.
150
+ //
151
+ VNFuncApp funcApp;
152
+ if (!vnStore->GetVNFunc (rii->domCmpNormVN , &funcApp))
153
+ {
154
+ return ;
155
+ }
156
+
157
+ genTreeOps const oper = genTreeOps (funcApp.m_func );
158
+
159
+ // Look for {EQ,NE}({AND,OR,NOT}, 0)
160
+ //
161
+ if (!GenTree::StaticOperIs (oper, GT_EQ, GT_NE))
162
+ {
163
+ return ;
164
+ }
165
+
166
+ const ValueNum constantVN = funcApp.m_args [1 ];
167
+ if (constantVN != vnStore->VNZeroForType (TYP_INT))
168
+ {
169
+ return ;
170
+ }
171
+
172
+ const ValueNum predVN = funcApp.m_args [0 ];
173
+ VNFuncApp predFuncApp;
174
+ if (!vnStore->GetVNFunc (predVN, &predFuncApp))
175
+ {
176
+ return ;
177
+ }
178
+
179
+ genTreeOps const predOper = genTreeOps (predFuncApp.m_func );
180
+
181
+ if (!GenTree::StaticOperIs (predOper, GT_AND, GT_OR, GT_NOT))
182
+ {
183
+ return ;
184
+ }
185
+
186
+ // Dominating compare is {EQ,NE}({AND,OR,NOT}, 0).
187
+ //
188
+ // See if one of {AND,OR,NOT} operands is related.
189
+ //
190
+ for (unsigned int i = 0 ; (i < predFuncApp.m_arity ) && !rii->canInfer ; i++)
191
+ {
192
+ ValueNum pVN = predFuncApp.m_args [i];
193
+
194
+ for (auto vnRelation : s_vnRelations)
195
+ {
196
+ const ValueNum relatedVN = vnStore->GetRelatedRelop (pVN, vnRelation);
197
+
198
+ if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == rii->treeNormVN ))
199
+ {
200
+ rii->vnRelation = vnRelation;
201
+ rii->canInfer = true ;
202
+
203
+ // If dom predicate is wrapped in EQ(*,0) then a true dom
204
+ // predicate implies a false branch outcome, and vice versa.
205
+ //
206
+ // And if the dom predicate is GT_NOT we reverse yet again.
207
+ //
208
+ rii->reverseSense = (oper == GT_EQ) ^ (predOper == GT_NOT);
209
+
210
+ // We only get partial knowledge in these cases.
211
+ //
212
+ // AND(p1,p2) = true ==> both p1 and p2 must be true
213
+ // AND(p1,p2) = false ==> don't know p1 or p2
214
+ // OR(p1,p2) = true ==> don't know p1 or p2
215
+ // OR(p1,p2) = false ==> both p1 and p2 must be false
216
+ //
217
+ if (predOper != GT_NOT)
218
+ {
219
+ rii->canInferFromFalse = rii->reverseSense ^ (predOper == GT_OR);
220
+ rii->canInferFromTrue = rii->reverseSense ^ (predOper == GT_AND);
221
+ }
222
+
223
+ JITDUMP (" Inferring predicate value from %s\n " , GenTree::OpName (predOper));
224
+ return ;
225
+ }
226
+ }
227
+ }
228
+ }
229
+
75
230
// ------------------------------------------------------------------------
76
231
// optRedundantBranch: try and optimize a possibly redundant branch
77
232
//
@@ -120,12 +275,25 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
120
275
return false ;
121
276
}
122
277
123
- const ValueNumStore::VN_RELATION_KIND vnRelations[] = {ValueNumStore::VN_RELATION_KIND::VRK_Same,
124
- ValueNumStore::VN_RELATION_KIND::VRK_Reverse,
125
- ValueNumStore::VN_RELATION_KIND::VRK_Swap,
126
- ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse} ;
278
+ // Unpack the tree's VN
279
+ //
280
+ ValueNum treeNormVN;
281
+ vnStore-> VNUnpackExc (tree-> GetVN (VNK_Liberal), &treeNormVN, &treeExcVN) ;
127
282
128
- while (domBlock != nullptr )
283
+ // If the treeVN is a constant, we optimize directly.
284
+ //
285
+ // Note the inferencing we do below is not valid for constant VNs,
286
+ // so handling/avoiding this case up front is a correctness requirement.
287
+ //
288
+ if (vnStore->IsVNConstant (treeNormVN))
289
+ {
290
+
291
+ relopValue = (treeNormVN == vnStore->VNZeroForType (TYP_INT)) ? 0 : 1 ;
292
+ JITDUMP (" Relop [%06u] " FMT_BB " has known value %s\n " , dspTreeID (tree), block->bbNum ,
293
+ relopValue == 0 ? " false" : " true" );
294
+ }
295
+
296
+ while ((relopValue == -1 ) && (domBlock != nullptr ))
129
297
{
130
298
// Check the current dominator
131
299
//
@@ -141,34 +309,15 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
141
309
// We can use liberal VNs here, as bounds checks are not yet
142
310
// manifest explicitly as relops.
143
311
//
144
- // Look for an exact match and also try the various swapped/reversed forms.
145
- //
146
- ValueNum treeNormVN;
147
- vnStore->VNUnpackExc (tree->GetVN (VNK_Liberal), &treeNormVN, &treeExcVN);
148
- ValueNum domCmpNormVN;
149
- vnStore->VNUnpackExc (domCmpTree->GetVN (VNK_Liberal), &domCmpNormVN, &domCmpExcVN);
150
- ValueNumStore::VN_RELATION_KIND vnRelationMatch = ValueNumStore::VN_RELATION_KIND::VRK_Same;
151
- bool matched = false ;
152
-
153
- for (auto vnRelation : vnRelations)
154
- {
155
- const ValueNum relatedVN = vnStore->GetRelatedRelop (domCmpNormVN, vnRelation);
156
- if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == treeNormVN))
157
- {
158
- vnRelationMatch = vnRelation;
159
- matched = true ;
160
- break ;
161
- }
162
- }
312
+ RelopImplicationInfo rii;
313
+ rii.treeNormVN = treeNormVN;
314
+ vnStore->VNUnpackExc (domCmpTree->GetVN (VNK_Liberal), &rii.domCmpNormVN , &domCmpExcVN);
163
315
164
- // Note we could also infer the tree relop's value from relops higher in the dom tree
165
- // that involve the same operands but are not swaps or reverses.
166
- //
167
- // For example, (x >= 0) dominating (x > 0).
316
+ // See if knowing the value of domCmpNormVN implies knowing the value of treeNormVN.
168
317
//
169
- // That is left as a future enhancement.
170
- //
171
- if (matched )
318
+ optRelopImpliesRelop (&rii);
319
+
320
+ if (rii. canInfer )
172
321
{
173
322
// If we have a long skinny dominator tree we may scale poorly,
174
323
// and in particular reachability (below) is costly. Give up if
@@ -184,20 +333,23 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
184
333
// Is there a unique path from the dominating compare?
185
334
//
186
335
JITDUMP (" \n Dominator " FMT_BB " of " FMT_BB " has relop with %s liberal VN\n " , domBlock->bbNum ,
187
- block->bbNum , ValueNumStore::VNRelationString (vnRelationMatch ));
336
+ block->bbNum , ValueNumStore::VNRelationString (rii. vnRelation ));
188
337
DISPTREE (domCmpTree);
189
338
JITDUMP (" Redundant compare; current relop:\n " );
190
339
DISPTREE (tree);
191
340
192
- const bool domIsSameRelop = (vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_Same) ||
193
- (vnRelationMatch == ValueNumStore::VN_RELATION_KIND::VRK_Swap);
341
+ const bool domIsSameRelop = (rii. vnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Same) ||
342
+ (rii. vnRelation == ValueNumStore::VN_RELATION_KIND::VRK_Swap);
194
343
195
344
BasicBlock* const trueSuccessor = domBlock->bbJumpDest ;
196
345
BasicBlock* const falseSuccessor = domBlock->bbNext ;
197
- const bool trueReaches = optReachable (trueSuccessor, block, domBlock);
198
- const bool falseReaches = optReachable (falseSuccessor, block, domBlock);
199
346
200
- if (trueReaches && falseReaches)
347
+ // If we can trace the flow from the dominating relop, we can infer its value.
348
+ //
349
+ const bool trueReaches = optReachable (trueSuccessor, block, domBlock);
350
+ const bool falseReaches = optReachable (falseSuccessor, block, domBlock);
351
+
352
+ if (trueReaches && falseReaches && rii.canInferFromTrue && rii.canInferFromFalse )
201
353
{
202
354
// Both dominating compare outcomes reach the current block so we can't infer the
203
355
// value of the relop.
@@ -212,22 +364,26 @@ bool Compiler::optRedundantBranch(BasicBlock* const block)
212
364
return true ;
213
365
}
214
366
}
215
- else if (trueReaches)
367
+ else if (trueReaches && !falseReaches && rii. canInferFromTrue )
216
368
{
217
369
// Taken jump in dominator reaches, fall through doesn't; relop must be true/false.
218
370
//
219
- JITDUMP (" Jump successor " FMT_BB " of " FMT_BB " reaches, relop must be %s\n " ,
220
- domBlock->bbJumpDest ->bbNum , domBlock->bbNum , domIsSameRelop ? " true" : " false" );
221
- relopValue = domIsSameRelop ? 1 : 0 ;
371
+ const bool relopIsTrue = rii.reverseSense ^ domIsSameRelop;
372
+ JITDUMP (" Jump successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n " ,
373
+ domBlock->bbJumpDest ->bbNum , domBlock->bbNum , dspTreeID (tree),
374
+ relopIsTrue ? " true" : " false" );
375
+ relopValue = relopIsTrue ? 1 : 0 ;
222
376
break ;
223
377
}
224
- else if (falseReaches)
378
+ else if (falseReaches && !trueReaches && rii. canInferFromFalse )
225
379
{
226
380
// Fall through from dominator reaches, taken jump doesn't; relop must be false/true.
227
381
//
228
- JITDUMP (" Fall through successor " FMT_BB " of " FMT_BB " reaches, relop must be %s\n " ,
229
- domBlock->bbNext ->bbNum , domBlock->bbNum , domIsSameRelop ? " false" : " true" );
230
- relopValue = domIsSameRelop ? 0 : 1 ;
382
+ const bool relopIsFalse = rii.reverseSense ^ domIsSameRelop;
383
+ JITDUMP (" Fall through successor " FMT_BB " of " FMT_BB " reaches, relop [%06u] must be %s\n " ,
384
+ domBlock->bbNext ->bbNum , domBlock->bbNum , dspTreeID (tree),
385
+ relopIsFalse ? " false" : " true" );
386
+ relopValue = relopIsFalse ? 0 : 1 ;
231
387
break ;
232
388
}
233
389
else
@@ -822,7 +978,6 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
822
978
//
823
979
if (stmt == block->firstStmt ())
824
980
{
825
- JITDUMP (" -- no, no prior stmt\n " );
826
981
return false ;
827
982
}
828
983
@@ -877,11 +1032,6 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
877
1032
ValueNumStore::VN_RELATION_KIND candidateVnRelation = ValueNumStore::VN_RELATION_KIND::VRK_Same;
878
1033
bool sideEffect = false ;
879
1034
880
- const ValueNumStore::VN_RELATION_KIND vnRelations[] = {ValueNumStore::VN_RELATION_KIND::VRK_Same,
881
- ValueNumStore::VN_RELATION_KIND::VRK_Reverse,
882
- ValueNumStore::VN_RELATION_KIND::VRK_Swap,
883
- ValueNumStore::VN_RELATION_KIND::VRK_SwapReverse};
884
-
885
1035
// We need to keep track of which locals might be killed by
886
1036
// the trees between the expression we want to forward substitute
887
1037
// and the jump.
@@ -1012,7 +1162,7 @@ bool Compiler::optRedundantRelop(BasicBlock* const block)
1012
1162
bool matched = false ;
1013
1163
ValueNumStore::VN_RELATION_KIND vnRelationMatch = ValueNumStore::VN_RELATION_KIND::VRK_Same;
1014
1164
1015
- for (auto vnRelation : vnRelations )
1165
+ for (auto vnRelation : s_vnRelations )
1016
1166
{
1017
1167
const ValueNum relatedVN = vnStore->GetRelatedRelop (domCmpVN, vnRelation);
1018
1168
if ((relatedVN != ValueNumStore::NoVN) && (relatedVN == treeVN))
0 commit comments