https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92926

            Bug ID: 92926
           Summary: Wrong code generated because of shared tree node in
                    gimplify
           Product: gcc
           Version: 10.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: amker at gcc dot gnu.org
  Target Milestone: ---

Following code is reduced from cppcoro but is irrelevant to coroutine.

#include <cassert>
#include <string>
 class ipv6_address
 {
 public:
  constexpr ipv6_address(
   std::uint16_t part0,
   std::uint16_t part1,
   std::uint16_t part2,
   std::uint16_t part3,
   std::uint16_t part4,
   std::uint16_t part5,
   std::uint16_t part6,
   std::uint16_t part7);

  static constexpr ipv6_address loopback();
  std::string to_string() const;

 private:
  alignas(std::uint64_t) std::uint8_t m_bytes[16];
 };

constexpr ipv6_address::ipv6_address(
  std::uint16_t part0,
  std::uint16_t part1,
  std::uint16_t part2,
  std::uint16_t part3,
  std::uint16_t part4,
  std::uint16_t part5,
  std::uint16_t part6,
  std::uint16_t part7)
  : m_bytes{
   static_cast<std::uint8_t>(part0 >> 8),
   static_cast<std::uint8_t>(part0),
   static_cast<std::uint8_t>(part1 >> 8),
   static_cast<std::uint8_t>(part1),
   static_cast<std::uint8_t>(part2 >> 8),
   static_cast<std::uint8_t>(part2),
   static_cast<std::uint8_t>(part3 >> 8),
   static_cast<std::uint8_t>(part3),
   static_cast<std::uint8_t>(part4 >> 8),
   static_cast<std::uint8_t>(part4),
   static_cast<std::uint8_t>(part5 >> 8),
   static_cast<std::uint8_t>(part5),
   static_cast<std::uint8_t>(part6 >> 8),
   static_cast<std::uint8_t>(part6),
   static_cast<std::uint8_t>(part7 >> 8),
   static_cast<std::uint8_t>(part7) }
{}

constexpr ipv6_address ipv6_address::loopback()
{
  return ipv6_address{ 0, 0, 0, 0, 0, 0, 0, 1 };
}

char hex_char(std::uint8_t value)
{
  return value < 10 ?
    static_cast<char>('0' + value) :
    static_cast<char>('a' + value - 10);
}

std::string ipv6_address::to_string() const
{
  std::uint32_t longestZeroRunStart = 0;
  std::uint32_t longestZeroRunLength = 0;
  for (std::uint32_t i = 0; i < 8; )
  {
    if (m_bytes[2 * i] == 0 && m_bytes[2 * i + 1] == 0)
    {
      const std::uint32_t zeroRunStart = i;
      ++i;
      while (i < 8 && m_bytes[2 * i] == 0 && m_bytes[2 * i + 1] == 0)
      {
        ++i;
      }

      std::uint32_t zeroRunLength = i - zeroRunStart;
      if (zeroRunLength > longestZeroRunLength)
      {
        longestZeroRunLength = zeroRunLength;
        longestZeroRunStart = zeroRunStart;
      }
    }
    else
    {
      ++i;
    }
  }

  char buffer[40];

  char* c = &buffer[0];

  auto appendPart = [&](std::uint32_t index)
  {
    const std::uint8_t highByte = m_bytes[index * 2];
    const std::uint8_t lowByte = m_bytes[index * 2 + 1];

    if (highByte > 0 || lowByte > 15)
    {
      if (highByte > 0)
      {
        if (highByte > 15)
        {
          *c++ = hex_char(highByte >> 4);
        }
        *c++ = hex_char(highByte & 0xF);
      }
      *c++ = hex_char(lowByte >> 4);
    }
    *c++ = hex_char(lowByte & 0xF);
  };

  if (longestZeroRunLength >= 2)
  {
    for (std::uint32_t i = 0; i < longestZeroRunStart; ++i)
    {
      if (i > 0)
      {
        *c++ = ':';
      }

      appendPart(i);
    }

    *c++ = ':';
    *c++ = ':';

    for (std::uint32_t i = longestZeroRunStart + longestZeroRunLength; i < 8;
++i)
    {
      appendPart(i);

      if (i < 7)
      {
        *c++ = ':';
      }
    }
  }
  else
  {
    appendPart(0);
    for (std::uint32_t i = 1; i < 8; ++i)
    {
      *c++ = ':';
      appendPart(i);
    }
  }

  assert((c - &buffer[0]) <= sizeof(buffer));

  return std::string{ &buffer[0], c };
}

std::string __attribute__((noinline)) foo ()
{
   return ipv6_address::loopback().to_string();
}

ipv6_address __attribute__((noinline)) bar ()
{
   return ipv6_address::loopback();
}

int main() {
  std::string s = foo ();
  ipv6_address a = bar ();
  assert(a.to_string() == s);
  return 0;
}

Compiling using following command line:
$ ./g++ -std=c++17 -m64 -O3 z.cc -o a.out
$ ./a.out
a.out: z.cc:168: int main(): Assertion `a.to_string() == s' failed.
Aborted

Root cause is ipv6_address::loopback as a constexpr function, what it returns
is folded into const ctor by C++ FE, also the const ctor is shared translation
unit wide by constexpr_call_table.  As a result the ctor as well as its vector
elements are shared between foo and bar.

In gimplify, CONSTRUCTOR_ELTS is optimized and cleared, causing shared node
changed in the other function.

Will send a patch for discussion.

Reply via email to