Bug 71957 - [5/6/7 Regression] Invalid code generation with function static objects
Summary: [5/6/7 Regression] Invalid code generation with function static objects
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 6.0
: P2 normal
Target Milestone: 5.5
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2016-07-21 11:31 UTC by Sebastian Huber
Modified: 2016-07-22 08:24 UTC (History)
1 user (show)

See Also:
Host:
Target: i?86-*-*
Build:
Known to work: 4.8.5, 4.9.3
Known to fail: 5.4.0, 6.1.0, 7.0
Last reconfirmed: 2016-07-21 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sebastian Huber 2016-07-21 11:31:47 UTC
The following test case produces wrong code with GCC 6. The problem is at least visible on x86, ARM, SPARC and PowerPC.

class A {
public:
  A(int);
};

template <typename T> class B {
public:
  virtual void* h(int) = 0;
};

template <typename T> class C : public B<T> {
public:
  explicit C(int);
  void* h(int);
};

C<A>& g(void);

class D : public A {
  static void* f(int);
};

void* D::f(int k) {
  B<D>& j = (reinterpret_cast<C<D>&>(g()));
  return j.h(k);
}

int f(void);

C<A>& g(void) {
  static C<A> i(f());
  return i;
}

g++ -Wfatal-errors -Wall -Wextra -S -O3 -fno-exceptions test.cc -o-
        .file   "test.cc"
        .text
        .align 2
        .p2align 4,,15
        .globl  _ZN1D1fEi
        .type   _ZN1D1fEi, @function
_ZN1D1fEi:
.LFB0:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $_ZGVZ1gvE1i, %edi
        movzbl  _ZGVZ1gvE1i(%rip), %eax
        call    __cxa_guard_acquire

<-- ERROR: It must check the return value of __cxa_guard_acquire()

        call    _Z1fv
        movl    $_ZZ1gvE1i, %edi
        movl    %eax, %esi
        call    _ZN1CI1AEC1Ei
        movl    $_ZGVZ1gvE1i, %edi
        call    __cxa_guard_release

<-- ERROR Invalid function return

        .cfi_endproc
.LFE0:
        .size   _ZN1D1fEi, .-_ZN1D1fEi
        .p2align 4,,15
        .globl  _Z1gv
        .type   _Z1gv, @function
_Z1gv:
.LFB1:
        .cfi_startproc
        movzbl  _ZGVZ1gvE1i(%rip), %eax
        testb   %al, %al
        je      .L15
        movl    $_ZZ1gvE1i, %eax
        ret
        .p2align 4,,10
        .p2align 3
.L15:
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        movl    $_ZGVZ1gvE1i, %edi
        call    __cxa_guard_acquire
        testl   %eax, %eax
        je      .L5
        call    _Z1fv
        movl    $_ZZ1gvE1i, %edi
        movl    %eax, %esi
        call    _ZN1CI1AEC1Ei
        movl    $_ZGVZ1gvE1i, %edi
        call    __cxa_guard_release
.L5:
        movl    $_ZZ1gvE1i, %eax
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE1:
        .size   _Z1gv, .-_Z1gv
        .local  _ZGVZ1gvE1i
        .comm   _ZGVZ1gvE1i,8,8
        .local  _ZZ1gvE1i
        .comm   _ZZ1gvE1i,8,8
        .ident  "GCC: (GNU) 6.1.1 20160705 [gcc-6-branch revision fcd04db:3fed107:ebf5486fa49b6660091b549d17e945f63e698eac]"
        .section        .note.GNU-stack,"",@progbits
Comment 1 Richard Biener 2016-07-21 11:58:00 UTC
Confirmed on i?86.
Comment 2 Richard Biener 2016-07-21 12:04:34 UTC
t.C.086t.ccp2:  __builtin_unreachable ();

looks like devirt to me and things go downhill from there.  -fno-devirtualize fixes it for me.

Honza?

  _9 = OBJ_TYPE_REF(_3;(struct B)&MEM[(void *)&i]->0) (&MEM[(void *)&i], k_7(D));

is folded to unreachable () inside D::f.

  <bb 3>:
  _11 = __cxa_guard_acquire (&_ZGVZ1gvE1i);
  if (_11 != 0)
    goto <bb 4>;
  else
    goto <bb 5>;

  <bb 4>:
  _12 = f ();
  C<A>::C (&i, _12);
  __cxa_guard_release (&_ZGVZ1gvE1i);

  <bb 5>:
  _2 = MEM[(struct B *)&i]._vptr.B;
  _3 = *_2;
  _9 = OBJ_TYPE_REF(_3;(struct B)&MEM[(void *)&i]->0) (&MEM[(void *)&i], k_7(D));
  return _9;
Comment 3 Richard Biener 2016-07-21 12:07:18 UTC
On a second look the testcase looks invalid as it invokes a virtual function
via C<D> on an object of type C<A>.  Why do you think doing this is valid?
Comment 4 Sebastian Huber 2016-07-21 12:34:52 UTC
(In reply to Richard Biener from comment #3)
> On a second look the testcase looks invalid as it invokes a virtual function
> via C<D> on an object of type C<A>.  Why do you think doing this is valid?

I try to generate a new test case without the reinterpret cast.
Comment 5 Sebastian Huber 2016-07-22 08:24:56 UTC
(In reply to Sebastian Huber from comment #4)
> (In reply to Richard Biener from comment #3)
> > On a second look the testcase looks invalid as it invokes a virtual function
> > via C<D> on an object of type C<A>.  Why do you think doing this is valid?
> 
> I try to generate a new test case without the reinterpret cast.

Sorry, you are right, this is undefined behaviour.  Without the reinterpret casts it is not reproducible.  I reduced the test case from a larger code base via the delta tool.  This code worked for years well.  Using the -fsanitize=unreachable option would have saved some trouble.