This is an archive of the discontinued LLVM Phabricator instance.

Revert "[InstCombine] Handle undef when pruning unreachable code"
AbandonedPublic

Authored by tsymalla on May 30 2023, 12:17 AM.

Details

Reviewers
foad
nikic
Summary

This reverts commit 1fc425380e9860a6beb53fa68d02e7fb14969963.
This is causing some test failures in cases like

for (;;) {
	callIntrinsicStore...
	switch (undef) {
		case 0:
			break;
	}
}

where LLVM is completely optimizing away the loop. The IR in mind is
something like the following.
So, independent of which jump target we take in 10, we will still be
able to reach the final merge block. With the optimization, the loop
body (add) wil be optimized away and we will never reach the merge
block.

entry:
  br label %0

0:
  br label %1, !llvm.loop

1:
  br label %2

2:
  %3 = phi i32 [ 0, %1 ], [ %14, %13 ]
  br label %4, !llvm.loop !3

4:                                                ; preds = %2
  %5 = icmp slt i32 %3, 1
  br i1 %5, label %6, label %15

6:                                                ; preds = %4
  br label %7

7:                                                ; preds = %6
  br label %8, !llvm.loop !4

8:                                                ; preds = %7
  br label %9

9:                                                ; preds = %8

  br label %10

10:                                               ; preds = %9
  br i1 undef, label %11, label %12

11:                                               ; preds = %10

  br label %15

12:                                               ; preds = %10

  br label %13

13:                                               ; preds = %12

  %14 = add i32 %3, 1
  br label %2, !llvm.loop !5

15:                                               ; preds = %11, %4
  br label %17

16:                                               ; No predecessors!
  br label %17

17:                                               ; preds = %15, %16
  call void (...) @write(...)
  ret void

Diff Detail

Event Timeline

tsymalla created this revision.May 30 2023, 12:17 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 30 2023, 12:17 AM
tsymalla requested review of this revision.May 30 2023, 12:17 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 30 2023, 12:17 AM
nikic requested changes to this revision.May 30 2023, 3:38 AM
nikic edited the summary of this revision. (Show Details)

Branch on undef is undefined behavior, see https://llvm.org/docs/LangRef.html#id33:

If ‘cond’ is poison or undef, this instruction has undefined behavior.

Either your source program has undefined behavior, or some optimization incorrectly introduced the branch on undef. If the latter, you may want to look through the print-after-all trace to identify when the br undef first appears. It's possible that some optimization is failing to freeze the condition.

This revision now requires changes to proceed.May 30 2023, 3:40 AM

Branch on undef is undefined behavior, see https://llvm.org/docs/LangRef.html#id33:

If ‘cond’ is poison or undef, this instruction has undefined behavior.

Either your source program has undefined behavior, or some optimization incorrectly introduced the branch on undef. If the latter, you may want to look through the print-after-all trace to identify when the br undef first appears. It's possible that some optimization is failing to freeze the condition.

I know branching and switching on undefs is undefined behavior, and I know that the undef branch is intended. In no way the whole loop as marked in the pseudo-code should be optimized away. What do you mean by "freezing" the condition?

nikic added a comment.May 30 2023, 4:00 AM

Branch on undef is undefined behavior, see https://llvm.org/docs/LangRef.html#id33:

If ‘cond’ is poison or undef, this instruction has undefined behavior.

Either your source program has undefined behavior, or some optimization incorrectly introduced the branch on undef. If the latter, you may want to look through the print-after-all trace to identify when the br undef first appears. It's possible that some optimization is failing to freeze the condition.

I know branching and switching on undefs is undefined behavior, and I know that the undef branch is intended. In no way the whole loop as marked in the pseudo-code should be optimized away.

I don't understand, why should the loop not be optimized away? Can you maybe provide a failing alive2 proof for the case you have in mind?

What do you mean by "freezing" the condition?

See https://llvm.org/docs/LangRef.html#freeze-instruction. Freezing the condition means that the branch will be non-deterministic rather than undefined.

Branch on undef is undefined behavior, see https://llvm.org/docs/LangRef.html#id33:

If ‘cond’ is poison or undef, this instruction has undefined behavior.

Either your source program has undefined behavior, or some optimization incorrectly introduced the branch on undef. If the latter, you may want to look through the print-after-all trace to identify when the br undef first appears. It's possible that some optimization is failing to freeze the condition.

I know branching and switching on undefs is undefined behavior, and I know that the undef branch is intended. In no way the whole loop as marked in the pseudo-code should be optimized away.

I don't understand, why should the loop not be optimized away? Can you maybe provide a failing alive2 proof for the case you have in mind?

In the above pseudo-code, which is the input into some of LLVMs generic passes, the store is executed at least once, independent of the inner branching that either breaks or does not break the loop.
That's what the final IR without your patch also resembles - it executes the store and does not loop.

What do you mean by "freezing" the condition?

See https://llvm.org/docs/LangRef.html#freeze-instruction. Freezing the condition means that the branch will be non-deterministic rather than undefined.

Thanks! Makes sense.

nikic added a comment.May 30 2023, 4:10 AM

Branch on undef is undefined behavior, see https://llvm.org/docs/LangRef.html#id33:

If ‘cond’ is poison or undef, this instruction has undefined behavior.

Either your source program has undefined behavior, or some optimization incorrectly introduced the branch on undef. If the latter, you may want to look through the print-after-all trace to identify when the br undef first appears. It's possible that some optimization is failing to freeze the condition.

I know branching and switching on undefs is undefined behavior, and I know that the undef branch is intended. In no way the whole loop as marked in the pseudo-code should be optimized away.

I don't understand, why should the loop not be optimized away? Can you maybe provide a failing alive2 proof for the case you have in mind?

In the above pseudo-code, which is the input into some of LLVMs generic passes, the store is executed at least once, independent of the inner branching that either breaks or does not break the loop.
That's what the final IR without your patch also resembles - it executes the store and does not loop.

I see. Whether the store will be dropped should depend on intrinsic attributes -- which intrinsic is doing the store in this case?

If the intrinsic is willreturn, then UB can propagate upwards past the store. The intrinsic needs to be non-willreturn if it should be retained in such a case.

tsymalla abandoned this revision.May 30 2023, 4:28 AM

Thanks! Closing this revision.