This is an archive of the discontinued LLVM Phabricator instance.

PR49585: Emit the jump destination for a for loop 'continue' from within the scope of the condition variable.
ClosedPublic

Authored by rsmith on Mar 17 2021, 2:09 PM.

Details

Summary

The condition variable is in scope in the loop increment, so we need to
emit the jump destination from wthin the scope of the condition
variable.

For GCC compatibility (and compatibility with real-world 'FOR_EACH'
macros), 'continue' is permitted in a statement expression within the
condition of a for loop, though, so there are two cases here:

  • If the for loop has no condition variable, we can emit the jump destination before emitting the condition.
  • If the for loop has a condition variable, we must defer emitting the jump destination until after emitting the variable. We diagnose a 'continue' appearing in the initializer of the condition variable, because it would jump past the initializer into the scope of that variable.

Diff Detail

Event Timeline

rsmith requested review of this revision.Mar 17 2021, 2:09 PM
rsmith created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptMar 17 2021, 2:09 PM

Note that this fixes a miscompile where we'd destroy a for loop condition variable twice when encountering a continue (once before the increment and again after).

rjmccall accepted this revision.Mar 17 2021, 3:58 PM

Well, that's certainly some bug. Patch LGTM.

This revision is now accepted and ready to land.Mar 17 2021, 3:58 PM
This revision was landed with ongoing or failed builds.Mar 17 2021, 4:24 PM
This revision was automatically updated to reflect the committed changes.

We're seeing a crash in the optimizer after this patch, with the following logged in assert builds: assert.h assertion failed at llvm/include/llvm/Support/Casting.h:104 in static bool llvm::isa_impl_cl<llvm::InvokeInst, const llvm::Instruction *>::doit(const From *) [To = llvm::InvokeInst, From = const llvm::Instruction *]: Val && "isa<> used on a null pointer"

The error message I posted earlier was when using O1 + new PM, but it crashes with the old one & no optimizations:

$ cat /tmp/repro.cc
void a() {
  for (; int b = 0;) continue;
}
$ clang -c /tmp/repro.cc -o /tmp/repro.o
Referring to a basic block in another function!
  br label %for.inc
in function _Z1av
fatal error: error in backend: Broken function found, compilation aborted!
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: clang -c /tmp/repro.cc -o /tmp/repro.o
1.      <eof> parser at end of file
2.      Per-function optimization
3.      Running pass 'Module Verifier' on function '@_Z1av'
...
$ clang -fno-legacy-pass-manager -O1 -c /tmp/repro.cc -o /tmp/repro.o
clang: /home/rupprecht/src/llvm-project/llvm/include/llvm/Support/Casting.h:104: static bool llvm::isa_impl_cl<llvm::InvokeInst, const llvm::Instruction *>::doit(const From *) [To = llvm::InvokeInst, From = const llvm::Instruction *]: Assertion `Val && "isa<> used on a null pointer"' failed.
PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: bin/clang -fno-legacy-pass-manager -O1 -c /tmp/repro.cc -o /tmp/repro.o
1.      <eof> parser at end of file
2.      Optimizer
...

I tried creating an IR repro, but: running -S -emit-llvm with the old PM crashes before it can generate anything, and running with the new PM seems to generate invalid IR (branches to %for.inc, which does not exist). I suspect this is not an optimizer bug, but crashing in the optimizer because invalid IR has been fed to it.

Invalid IR generation should be addressed by 19d2c65ddd757997785163709800f837857f686d