Skip to content

Commit 67bb749

Browse files
committed
Auto merge of #130775 - jieyouxu:revert-129047, r=nagisa
Revert "Apply EarlyOtherwiseBranch to scalar value #129047" This reverts PR #129047, commit a772336, reversing changes made to 702987f. cc `@DianQK` and `@cjgillot` as the PR author and reviewer of #129047 respectively. It seems [Apply EarlyOtherwiseBranch to scalar value #129047](#129047) may have lead to several nightly regressions: - #130769 - #130774 - #130771 Example test that would ICE with changes in #129047 (this test is included in this PR): ```rs //@ compile-flags: -C opt-level=3 //@ check-pass use std::task::Poll; pub fn poll(val: Poll<Result<Option<Vec<u8>>, u8>>) { match val { Poll::Ready(Ok(Some(_trailers))) => {} Poll::Ready(Err(_err)) => {} Poll::Ready(Ok(None)) => {} Poll::Pending => {} } } ``` Since this is a mir-opt ICE that seems to quite easy to trigger with real-world crates being affected, let's revert for now and reland the mir-opt after these are fixed.
2 parents 4cbfcf1 + ad7eb48 commit 67bb749

6 files changed

+103
-422
lines changed

compiler/rustc_mir_transform/src/early_otherwise_branch.rs

+85-175
Original file line numberDiff line numberDiff line change
@@ -133,29 +133,18 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
133133

134134
let mut patch = MirPatch::new(body);
135135

136-
let (second_discriminant_temp, second_operand) = if opt_data.need_hoist_discriminant {
137-
// create temp to store second discriminant in, `_s` in example above
138-
let second_discriminant_temp =
139-
patch.new_temp(opt_data.child_ty, opt_data.child_source.span);
136+
// create temp to store second discriminant in, `_s` in example above
137+
let second_discriminant_temp =
138+
patch.new_temp(opt_data.child_ty, opt_data.child_source.span);
140139

141-
patch.add_statement(
142-
parent_end,
143-
StatementKind::StorageLive(second_discriminant_temp),
144-
);
140+
patch.add_statement(parent_end, StatementKind::StorageLive(second_discriminant_temp));
145141

146-
// create assignment of discriminant
147-
patch.add_assign(
148-
parent_end,
149-
Place::from(second_discriminant_temp),
150-
Rvalue::Discriminant(opt_data.child_place),
151-
);
152-
(
153-
Some(second_discriminant_temp),
154-
Operand::Move(Place::from(second_discriminant_temp)),
155-
)
156-
} else {
157-
(None, Operand::Copy(opt_data.child_place))
158-
};
142+
// create assignment of discriminant
143+
patch.add_assign(
144+
parent_end,
145+
Place::from(second_discriminant_temp),
146+
Rvalue::Discriminant(opt_data.child_place),
147+
);
159148

160149
// create temp to store inequality comparison between the two discriminants, `_t` in
161150
// example above
@@ -164,9 +153,11 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
164153
let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span);
165154
patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp));
166155

167-
// create inequality comparison
168-
let comp_rvalue =
169-
Rvalue::BinaryOp(nequal, Box::new((parent_op.clone(), second_operand)));
156+
// create inequality comparison between the two discriminants
157+
let comp_rvalue = Rvalue::BinaryOp(
158+
nequal,
159+
Box::new((parent_op.clone(), Operand::Move(Place::from(second_discriminant_temp)))),
160+
);
170161
patch.add_statement(
171162
parent_end,
172163
StatementKind::Assign(Box::new((Place::from(comp_temp), comp_rvalue))),
@@ -202,13 +193,8 @@ impl<'tcx> crate::MirPass<'tcx> for EarlyOtherwiseBranch {
202193
TerminatorKind::if_(Operand::Move(Place::from(comp_temp)), true_case, false_case),
203194
);
204195

205-
if let Some(second_discriminant_temp) = second_discriminant_temp {
206-
// generate StorageDead for the second_discriminant_temp not in use anymore
207-
patch.add_statement(
208-
parent_end,
209-
StatementKind::StorageDead(second_discriminant_temp),
210-
);
211-
}
196+
// generate StorageDead for the second_discriminant_temp not in use anymore
197+
patch.add_statement(parent_end, StatementKind::StorageDead(second_discriminant_temp));
212198

213199
// Generate a StorageDead for comp_temp in each of the targets, since we moved it into
214200
// the switch
@@ -236,7 +222,6 @@ struct OptimizationData<'tcx> {
236222
child_place: Place<'tcx>,
237223
child_ty: Ty<'tcx>,
238224
child_source: SourceInfo,
239-
need_hoist_discriminant: bool,
240225
}
241226

242227
fn evaluate_candidate<'tcx>(
@@ -250,128 +235,70 @@ fn evaluate_candidate<'tcx>(
250235
return None;
251236
};
252237
let parent_ty = parent_discr.ty(body.local_decls(), tcx);
238+
if !bbs[targets.otherwise()].is_empty_unreachable() {
239+
// Someone could write code like this:
240+
// ```rust
241+
// let Q = val;
242+
// if discriminant(P) == otherwise {
243+
// let ptr = &mut Q as *mut _ as *mut u8;
244+
// // It may be difficult for us to effectively determine whether values are valid.
245+
// // Invalid values can come from all sorts of corners.
246+
// unsafe { *ptr = 10; }
247+
// }
248+
//
249+
// match P {
250+
// A => match Q {
251+
// A => {
252+
// // code
253+
// }
254+
// _ => {
255+
// // don't use Q
256+
// }
257+
// }
258+
// _ => {
259+
// // don't use Q
260+
// }
261+
// };
262+
// ```
263+
//
264+
// Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant
265+
// of an invalid value, which is UB.
266+
// In order to fix this, **we would either need to show that the discriminant computation of
267+
// `place` is computed in all branches**.
268+
// FIXME(#95162) For the moment, we adopt a conservative approach and
269+
// consider only the `otherwise` branch has no statements and an unreachable terminator.
270+
return None;
271+
}
253272
let (_, child) = targets.iter().next()?;
254-
255-
let Terminator {
256-
kind: TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr },
257-
source_info,
258-
} = bbs[child].terminator()
273+
let child_terminator = &bbs[child].terminator();
274+
let TerminatorKind::SwitchInt { targets: child_targets, discr: child_discr } =
275+
&child_terminator.kind
259276
else {
260277
return None;
261278
};
262279
let child_ty = child_discr.ty(body.local_decls(), tcx);
263280
if child_ty != parent_ty {
264281
return None;
265282
}
266-
267-
// We only handle:
268-
// ```
269-
// bb4: {
270-
// _8 = discriminant((_3.1: Enum1));
271-
// switchInt(move _8) -> [2: bb7, otherwise: bb1];
272-
// }
273-
// ```
274-
// and
275-
// ```
276-
// bb2: {
277-
// switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1];
278-
// }
279-
// ```
280-
if bbs[child].statements.len() > 1 {
283+
let Some(StatementKind::Assign(boxed)) = &bbs[child].statements.first().map(|x| &x.kind) else {
281284
return None;
282-
}
283-
284-
// When thie BB has exactly one statement, this statement should be discriminant.
285-
let need_hoist_discriminant = bbs[child].statements.len() == 1;
286-
let child_place = if need_hoist_discriminant {
287-
if !bbs[targets.otherwise()].is_empty_unreachable() {
288-
// Someone could write code like this:
289-
// ```rust
290-
// let Q = val;
291-
// if discriminant(P) == otherwise {
292-
// let ptr = &mut Q as *mut _ as *mut u8;
293-
// // It may be difficult for us to effectively determine whether values are valid.
294-
// // Invalid values can come from all sorts of corners.
295-
// unsafe { *ptr = 10; }
296-
// }
297-
//
298-
// match P {
299-
// A => match Q {
300-
// A => {
301-
// // code
302-
// }
303-
// _ => {
304-
// // don't use Q
305-
// }
306-
// }
307-
// _ => {
308-
// // don't use Q
309-
// }
310-
// };
311-
// ```
312-
//
313-
// Hoisting the `discriminant(Q)` out of the `A` arm causes us to compute the discriminant of an
314-
// invalid value, which is UB.
315-
// In order to fix this, **we would either need to show that the discriminant computation of
316-
// `place` is computed in all branches**.
317-
// FIXME(#95162) For the moment, we adopt a conservative approach and
318-
// consider only the `otherwise` branch has no statements and an unreachable terminator.
319-
return None;
320-
}
321-
// Handle:
322-
// ```
323-
// bb4: {
324-
// _8 = discriminant((_3.1: Enum1));
325-
// switchInt(move _8) -> [2: bb7, otherwise: bb1];
326-
// }
327-
// ```
328-
let [
329-
Statement {
330-
kind: StatementKind::Assign(box (_, Rvalue::Discriminant(child_place))),
331-
..
332-
},
333-
] = bbs[child].statements.as_slice()
334-
else {
335-
return None;
336-
};
337-
*child_place
338-
} else {
339-
// Handle:
340-
// ```
341-
// bb2: {
342-
// switchInt((_3.1: u64)) -> [1: bb5, otherwise: bb1];
343-
// }
344-
// ```
345-
let Operand::Copy(child_place) = child_discr else {
346-
return None;
347-
};
348-
*child_place
349285
};
350-
let destination = if need_hoist_discriminant || bbs[targets.otherwise()].is_empty_unreachable()
351-
{
352-
child_targets.otherwise()
353-
} else {
354-
targets.otherwise()
286+
let (_, Rvalue::Discriminant(child_place)) = &**boxed else {
287+
return None;
355288
};
289+
let destination = child_targets.otherwise();
356290

357291
// Verify that the optimization is legal for each branch
358292
for (value, child) in targets.iter() {
359-
if !verify_candidate_branch(
360-
&bbs[child],
361-
value,
362-
child_place,
363-
destination,
364-
need_hoist_discriminant,
365-
) {
293+
if !verify_candidate_branch(&bbs[child], value, *child_place, destination) {
366294
return None;
367295
}
368296
}
369297
Some(OptimizationData {
370298
destination,
371-
child_place,
299+
child_place: *child_place,
372300
child_ty,
373-
child_source: *source_info,
374-
need_hoist_discriminant,
301+
child_source: child_terminator.source_info,
375302
})
376303
}
377304

@@ -380,48 +307,31 @@ fn verify_candidate_branch<'tcx>(
380307
value: u128,
381308
place: Place<'tcx>,
382309
destination: BasicBlock,
383-
need_hoist_discriminant: bool,
384310
) -> bool {
385-
// In order for the optimization to be correct, the terminator must be a `SwitchInt`.
386-
let TerminatorKind::SwitchInt { discr: switch_op, targets } = &branch.terminator().kind else {
387-
return false;
388-
};
389-
if need_hoist_discriminant {
390-
// If we need hoist discriminant, the branch must have exactly one statement.
391-
let [statement] = branch.statements.as_slice() else {
392-
return false;
393-
};
394-
// The statement must assign the discriminant of `place`.
395-
let StatementKind::Assign(box (discr_place, Rvalue::Discriminant(from_place))) =
396-
statement.kind
397-
else {
398-
return false;
399-
};
400-
if from_place != place {
401-
return false;
402-
}
403-
// The assignment must invalidate a local that terminate on a `SwitchInt`.
404-
if !discr_place.projection.is_empty() || *switch_op != Operand::Move(discr_place) {
405-
return false;
406-
}
311+
// In order for the optimization to be correct, the branch must...
312+
// ...have exactly one statement
313+
if let [statement] = branch.statements.as_slice()
314+
// ...assign the discriminant of `place` in that statement
315+
&& let StatementKind::Assign(boxed) = &statement.kind
316+
&& let (discr_place, Rvalue::Discriminant(from_place)) = &**boxed
317+
&& *from_place == place
318+
// ...make that assignment to a local
319+
&& discr_place.projection.is_empty()
320+
// ...terminate on a `SwitchInt` that invalidates that local
321+
&& let TerminatorKind::SwitchInt { discr: switch_op, targets, .. } =
322+
&branch.terminator().kind
323+
&& *switch_op == Operand::Move(*discr_place)
324+
// ...fall through to `destination` if the switch misses
325+
&& destination == targets.otherwise()
326+
// ...have a branch for value `value`
327+
&& let mut iter = targets.iter()
328+
&& let Some((target_value, _)) = iter.next()
329+
&& target_value == value
330+
// ...and have no more branches
331+
&& iter.next().is_none()
332+
{
333+
true
407334
} else {
408-
// If we don't need hoist discriminant, the branch must not have any statements.
409-
if !branch.statements.is_empty() {
410-
return false;
411-
}
412-
// The place on `SwitchInt` must be the same.
413-
if *switch_op != Operand::Copy(place) {
414-
return false;
415-
}
335+
false
416336
}
417-
// It must fall through to `destination` if the switch misses.
418-
if destination != targets.otherwise() {
419-
return false;
420-
}
421-
// It must have exactly one branch for value `value` and have no more branches.
422-
let mut iter = targets.iter();
423-
let (Some((target_value, _)), None) = (iter.next(), iter.next()) else {
424-
return false;
425-
};
426-
target_value == value
427337
}

0 commit comments

Comments
 (0)