Bug 104777 - [9/10 Regression] gcc crashes while compiling a custom coroutine library sample
Summary: [9/10 Regression] gcc crashes while compiling a custom coroutine library sample
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: rtl-optimization (show other bugs)
Version: 11.2.0
: P2 normal
Target Milestone: 9.5
Assignee: Marek Polacek
URL:
Keywords: ice-on-valid-code, inline-asm
Depends on:
Blocks:
 
Reported: 2022-03-04 07:45 UTC by Janez Zemva
Modified: 2022-06-13 16:30 UTC (History)
6 users (show)

See Also:
Host:
Target: x86_64-linux-gnu
Build:
Known to work: 7.5.0, 8.1.0, 8.3.0
Known to fail: 8.4.0, 8.5.0, 9.1.0, 9.4.0
Last reconfirmed: 2022-03-04 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Janez Zemva 2022-03-04 07:45:14 UTC
The code sample is here https://github.com/user1095108/cr. gcc crashes only if the optimization level is higher than 2.

g++ -I.. -Ofast -no-pie -std=c++20 loop.cpp -o l

clang compiles everything without issue.

./lduring RTL pass: sched2
In file included from /usr/include/c++/11.2.0/functional:59,
                 from /usr/include/c++/11.2.0/pstl/glue_algorithm_defs.h:13,
                 from /usr/include/c++/11.2.0/algorithm:74,
                 from loop.hpp:5,
                 from loop.cpp:3:
/usr/include/c++/11.2.0/bits/std_function.h: In static member function 'static _Res std::_Function_handler<_Res(_ArgTypes ...), _Functor>::_M_invoke(const std::_Any_data&, _ArgTypes&& ...) [with _Res = void; _Functor = main()::<lambda()>; _ArgTypes = {}]':
/usr/include/c++/11.2.0/bits/std_function.h:293:7: internal compiler error: in move_insn, at haifa-sched.c:5471
  293 |       }
      |       ^
0xe4c988 internal_error(char const*, ...)
        ???:0
0xe42d02 fancy_abort(char const*, int, char const*)
        ???:0
0x13ee792 schedule_block(basic_block_def**, void*)
        ???:0
0x13da4f2 schedule_insns()
        ???:0
Please submit a full bug report,
with preprocessed source if appropriate.
Please include the complete backtrace with any bug report.
See <https://bugs.archlinux.org/> for instructions.
Comment 1 Martin Liška 2022-03-04 12:54:42 UTC
Confirmed, working on that.
Comment 2 Martin Liška 2022-03-04 15:44:37 UTC
Reduced test-case:

$ cat ice.ii
namespace std {
class tuple;
template <typename> auto declval() -> decltype(0);
template <typename> using remove_reference_t = int;
} // namespace std
extern "C" void abort();
namespace std {
template <int, typename _Tp> using tuple_element_t = _Tp;
template <typename _Fn> void __invoke_impl(_Fn __f) { __f(); }
template <typename, typename _Callable> void __invoke_r(_Callable __fn) {
  __invoke_impl(__fn);
}
template <typename> class function;
template <typename _Functor> struct _Base_manager {
  static _Functor *_M_get_pointer(int) { return 0; }
};
template <typename, typename> class _Function_handler;
template <typename _Res, typename _Functor, typename... _ArgTypes>
struct _Function_handler<_Res(_ArgTypes...), _Functor> {
  using _Base = _Base_manager<_Functor>;
  static _Res _M_invoke(const int &__functor) {
    auto __trans_tmp_1 = _Base::_M_get_pointer(__functor);
    __invoke_r<_Res>(*__trans_tmp_1);
    return _Res();
  }
};
template <typename _Res, typename... _ArgTypes>
struct function<_Res(_ArgTypes...)> {
  template <typename _Functor>
  using _Handler = _Function_handler<_Res(), _Functor>;
  function() {}
  template <typename _Functor> function(_Functor) {
    using _My_handler = _Handler<_Functor>;
    _M_invoker = _My_handler::_M_invoke;
  }
  using _Invoker_type = _Res (*)(const int &);
  _Invoker_type _M_invoker;
};
} // namespace std
bool savestate_r;
int savestate_ssb;
template <typename T> struct list {
  using value_type = T;
  struct node {
    value_type v_;
  } * last_;
  void emplace_back(auto... a) { last_ = new node(a...); }
};
namespace cr {
namespace detail {
template <long I> using at_t = std::tuple_element_t<I, std::tuple>;
}
struct task {
  std::function<void()> f_;
  void *r_;
  task(auto f) { f_ = f; }
  template <typename> auto return_ref() {
    return *static_cast<std::remove_reference_t<int> *>(r_);
  }
};
struct {
  task *pt_;
  list<task> l_;
  auto previous_task() { return pt_; }
  auto run(auto... f) -> decltype(std::declval<detail::at_t<sizeof...(f)>>()) {
    (l_.emplace_back(f), ...);
    return 0;
  }
} thread_local loop;
auto await(auto f) {
  using R = decltype(f);
  asm("" : "=m"(savestate_ssb), "=r"(savestate_r));
  if (savestate_r) {
    auto pt(loop.previous_task());
    return pt->return_ref<R>();
  }
  asm("" : : ""(loop));
  abort();
}
} // namespace cr

int
main() {
  cr::loop.run([] { cr::await([] {}) << cr::await([] {}); });
  return 0;
}

Started with -O2 -std=c++2a since r10-5137-g43aae289866f5ea5.
Comment 3 Marek Polacek 2022-03-04 15:51:36 UTC
Weird.  Mine then.
Comment 4 Andrew Pinski 2022-03-05 21:45:57 UTC
Does not crash for aarch64-linux-gnu.  Maybe some latent bug the change introduced.
Comment 5 Andrew Pinski 2022-03-05 21:55:23 UTC
Here is a C testcase which shows this is a latent bug (derived from the gimple of the reduced testcase in comment #2):

_Bool savestate_r;
int savestate_ssb;
extern  void abort();
__thread
struct {
  int t;
  int tt[1];
}  loop;
void f (const int * __functor)
{
  _Bool savestate_r0_5;
  _Bool savestate_r1_6;

  __asm__("" : "=m" (savestate_ssb), "=r" (savestate_r));
  savestate_r0_5 = savestate_r;
  if (savestate_r0_5 != 0)
    goto bb3;
  else
    goto bb4;

  bb3:
  __asm__("" : "=m" (savestate_ssb), "=r" (savestate_r));
  savestate_r1_6 = savestate_r;
  if (savestate_r1_6 != 0)
    goto bb6;
  else
    goto bb5;

bb4:
  __asm__ __volatile__("" :  : "m" (loop));
  abort ();

bb5:
  __asm__ __volatile__("" :  : "m" (loop));
  abort ();

bb6:
  return;

}
Comment 6 Andrew Pinski 2022-03-05 22:07:17 UTC
Note loop being a thread_local (__thread) is important here to get the crash.
Further reduced testcase:
int savestate_r;
int savestate_ssb;
extern void abort();
__thread int  loop;
void f (void)
{
  int savestate_r0_5;
  int savestate_r1_6;

  __asm__("" : "=m" (savestate_ssb), "=r" (savestate_r));
  savestate_r0_5 = savestate_r;
  if (savestate_r0_5 == 0)
  {
    __asm__ __volatile__("" :  : "m" (loop));
    abort ();
  }

  __asm__("" : "=m" (savestate_ssb), "=r" (savestate_r));
  savestate_r1_6 = savestate_r;
  if (savestate_r1_6 != 0)
    return;

  __asm__ __volatile__("" :  : "m" (loop));
  abort ();

}
Comment 7 Marek Polacek 2022-03-08 00:07:18 UTC
Comment 6 testcase started with r270550.

I've posted a patch.
Comment 8 GCC Commits 2022-03-08 19:00:54 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:e1133c0205a7e2a65834a1af780b8da15eead2a9

commit r12-7540-ge1133c0205a7e2a65834a1af780b8da15eead2a9
Author: Marek Polacek <polacek@redhat.com>
Date:   Mon Mar 7 16:15:46 2022 -0500

    rtl: ICE with thread_local and inline asm  [PR104777]
    
    In r270550, Jakub fixed classify_insn to handle asm goto: if the asm can
    jump to a label, the insn should be a JUMP_INSN.
    
    However, as the following testcase shows, non-null ASM_OPERANDS_LABEL_VEC
    doesn't guarantee that the rtx has any actual labels it can branch to.
    Here, the rtvec has 0 elements because expand_asm_stmt created it:
    
      rtvec labelvec = rtvec_alloc (nlabels); // nlabels == 0
    
    This causes an ICE in update_br_prob_note: BRANCH_EDGE (bb) crashes
    because there's no branch edge.  I think we can fix this by checking
    that there is at least one label the asm can jump to before wrapping
    the ASM_OPERANDS in a JUMP_INSN.
    
            PR rtl-optimization/104777
    
    gcc/ChangeLog:
    
            * rtl.cc (classify_insn): For ASM_OPERANDS, return JUMP_INSN only if
            ASM_OPERANDS_LABEL_VEC has at least one element.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/torture/tls/pr104777.c: New test.
Comment 9 Marek Polacek 2022-03-08 19:01:47 UTC
Fixed on trunk so far.
Comment 10 GCC Commits 2022-03-10 18:33:30 UTC
The releases/gcc-11 branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:d66c45fdc14bf88f7390a75129462640c59ce48e

commit r11-9647-gd66c45fdc14bf88f7390a75129462640c59ce48e
Author: Marek Polacek <polacek@redhat.com>
Date:   Mon Mar 7 16:15:46 2022 -0500

    rtl: ICE with thread_local and inline asm  [PR104777]
    
    In r270550, Jakub fixed classify_insn to handle asm goto: if the asm can
    jump to a label, the insn should be a JUMP_INSN.
    
    However, as the following testcase shows, non-null ASM_OPERANDS_LABEL_VEC
    doesn't guarantee that the rtx has any actual labels it can branch to.
    Here, the rtvec has 0 elements because expand_asm_stmt created it:
    
      rtvec labelvec = rtvec_alloc (nlabels); // nlabels == 0
    
    This causes an ICE in update_br_prob_note: BRANCH_EDGE (bb) crashes
    because there's no branch edge.  I think we can fix this by checking
    that there is at least one label the asm can jump to before wrapping
    the ASM_OPERANDS in a JUMP_INSN.
    
            PR rtl-optimization/104777
    
    gcc/ChangeLog:
    
            * rtl.c (classify_insn): For ASM_OPERANDS, return JUMP_INSN only if
            ASM_OPERANDS_LABEL_VEC has at least one element.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/torture/tls/pr104777.c: New test.
    
    (cherry picked from commit e1133c0205a7e2a65834a1af780b8da15eead2a9)
Comment 11 Marek Polacek 2022-03-10 18:34:23 UTC
Fixed.
Comment 12 Uroš Bizjak 2022-06-13 10:04:23 UTC
*** Bug 105936 has been marked as a duplicate of this bug. ***
Comment 13 Uroš Bizjak 2022-06-13 12:00:45 UTC
Please backport the patch also to gcc-10 branch.
Comment 14 Stas Sergeev 2022-06-13 12:19:44 UTC
(In reply to Uroš Bizjak from comment #13)
> Please backport the patch also to gcc-10 branch.

9.4.0 fails for me on ubuntu-20.
8.5.0 also fails.
Please back-port to all possible
branches.
Comment 15 Jakub Jelinek 2022-06-13 13:26:04 UTC
9.x and 8.x are not supported anymore, nothing can be backported to those (upstream, some vendors do support those on their own).
Comment 16 Marek Polacek 2022-06-13 14:39:23 UTC
I'll backport to 10.
Comment 17 GCC Commits 2022-06-13 16:29:51 UTC
The releases/gcc-10 branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:f2851a7cff4d74edca26d39c7bfa1264355a22ed

commit r10-10828-gf2851a7cff4d74edca26d39c7bfa1264355a22ed
Author: Marek Polacek <polacek@redhat.com>
Date:   Mon Mar 7 16:15:46 2022 -0500

    rtl: ICE with thread_local and inline asm  [PR104777]
    
    In r270550, Jakub fixed classify_insn to handle asm goto: if the asm can
    jump to a label, the insn should be a JUMP_INSN.
    
    However, as the following testcase shows, non-null ASM_OPERANDS_LABEL_VEC
    doesn't guarantee that the rtx has any actual labels it can branch to.
    Here, the rtvec has 0 elements because expand_asm_stmt created it:
    
      rtvec labelvec = rtvec_alloc (nlabels); // nlabels == 0
    
    This causes an ICE in update_br_prob_note: BRANCH_EDGE (bb) crashes
    because there's no branch edge.  I think we can fix this by checking
    that there is at least one label the asm can jump to before wrapping
    the ASM_OPERANDS in a JUMP_INSN.
    
            PR rtl-optimization/104777
    
    gcc/ChangeLog:
    
            * rtl.c (classify_insn): For ASM_OPERANDS, return JUMP_INSN only if
            ASM_OPERANDS_LABEL_VEC has at least one element.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/torture/tls/pr104777.c: New test.
    
    (cherry picked from commit e1133c0205a7e2a65834a1af780b8da15eead2a9)
Comment 18 Marek Polacek 2022-06-13 16:30:20 UTC
Done.