Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++] basic_ios<wchar_t> cannot store fill character WCHAR_MAX #89305

Merged
merged 24 commits into from
Jul 17, 2024

Conversation

xingxue-ibm
Copy link
Contributor

@xingxue-ibm xingxue-ibm commented Apr 18, 2024

libcxx std::basic_ios uses WEOF to indicate the fill value is uninitialized. On some platforms (e.g AIX and zOS in 64-bit mode) wchar_t is 4 bytes unsigned and wint_t is also 4 bytes which means WEOF cannot be distinguished from WCHAR_MAX by std::char_traits<wchar_t>::eq_int_type(), meaning this valid character value cannot be stored on affected platforms (as the implementation triggers reinitialization to widen(’ ’)).

This patch introduces a new helper class _FillHelper uses a boolean variable to indicate whether the fill character has been initialized, which is used by default in libcxx ABI version 2. The patch does not affect ABI version 1 except for targets AIX in 32- and 64-bit and z/OS in 64-bit (so that the layout of the implementation is compatible with the current IBM system provided libc++)

This is a continuation of Phabricator patch D124555. This patch uses a modified version of the approach suggested by @ldionne .

@xingxue-ibm xingxue-ibm added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Apr 18, 2024
@xingxue-ibm xingxue-ibm self-assigned this Apr 18, 2024
@xingxue-ibm xingxue-ibm requested a review from a team as a code owner April 18, 2024 20:37
@llvmbot
Copy link
Collaborator

llvmbot commented Apr 18, 2024

@llvm/pr-subscribers-libcxx

Author: Xing Xue (xingxue-ibm)

Changes

This is a continuation of Phabricator patch D124555.

libcxx std::basic_ios uses WEOF to indicate that the fill value is uninitialized. However, on AIX and zOS in 64-bit mode, wchar_t is 4 bytes unsigned and wint_t is also 4 bytes. which cannot be distinguished from WCHAR_MAX by std::char_traits&lt;wchar_t&gt;::eq_int_type().

The constraints of the C++ standard impose:

  • Cannot map any wchar_t value to WEOF.
  • All wchar_t values must map uniquely.

Compatibility with the platform C ABI imposes:

  • wint_t and wchar_t have the same width on AIX and z/OS in 64-bit mode.

The library is implemented correctly with respect to the C++ standard, however, this requirement imposed by C++ on C types is not also imposed by C. An implementation cannot be compliant where wchar_t is unsigned and the same size as wint_t. The current IBM system provided libc++ on AIX and zOS added a member __fill_set_ in class basic_ios to indicate whether or not the fill character has been initialized. __fill_set_ was used for both 32- and 64-bit on AIX and 64-bit on z/OS although this member could have been added more selectively for 64-bit only.

This patch uses the [approach](https://reviews.llvm.org/D124555#3566746) suggested by @ldionne. Class _OptionalFill is used for targets where a variable is needed to indicate whether the fill character has been initialized. The layout of the implementation is compatible with the current IBM system provided libc++. In the interest of ABI compatibility, we use it for 32-bit AIX as well.

Existing targets where this would break the ABI compatibility can opt out by setting _OptOutForABICompat to true.


Full diff: https://github.com/llvm/llvm-project/pull/89305.diff

2 Files Affected:

  • (modified) libcxx/include/ios (+49-6)
  • (added) libcxx/test/std/input.output/iostream.format/std.manip/setfill_eof.pass.cpp (+31)
diff --git a/libcxx/include/ios b/libcxx/include/ios
index 00c1d5c2d4bc58..bc31f30f14acf0 100644
--- a/libcxx/include/ios
+++ b/libcxx/include/ios
@@ -224,6 +224,7 @@ storage-class-specifier const error_category& iostream_category() noexcept;
 #include <__system_error/error_code.h>
 #include <__system_error/error_condition.h>
 #include <__system_error/system_error.h>
+#include <__type_traits/conditional.h>
 #include <__utility/swap.h>
 #include <__verbose_abort>
 #include <version>
@@ -521,6 +522,31 @@ inline _LIBCPP_HIDE_FROM_ABI void ios_base::exceptions(iostate __iostate) {
   clear(__rdstate_);
 }
 
+template <class _Traits>
+// Attribute 'packed' is used to keep the layout compatible with the previous
+// definition of '__set_' in basic_ios on AIX.
+struct _LIBCPP_PACKED _OptionalFill {
+  _OptionalFill() : __set_(false) { }
+  _OptionalFill& operator=(typename _Traits::int_type __x) { __set_ = true; __fill_val_ = __x; return *this; }
+  bool __is_set() const { return __set_; }
+  typename _Traits::int_type __fill() const { return __fill_val_; }
+
+private:
+  typename _Traits::int_type __fill_val_;
+  bool __set_;
+};
+
+template <class _Traits>
+struct _LIBCPP_PACKED _SentinelValueFill {
+  _SentinelValueFill() : __fill_val_(_Traits::eof()) { }
+  _SentinelValueFill& operator=(typename _Traits::int_type __x) { __fill_val_ = __x; return *this; }
+  bool __is_set() const { return __fill_val_ != _Traits::eof(); }
+  typename _Traits::int_type __fill() const { return __fill_val_; }
+
+private:
+  typename _Traits::int_type __fill_val_;
+};
+
 template <class _CharT, class _Traits>
 class _LIBCPP_TEMPLATE_VIS basic_ios : public ios_base {
 public:
@@ -590,7 +616,24 @@ protected:
 
 private:
   basic_ostream<char_type, traits_type>* __tie_;
-  mutable int_type __fill_;
+
+#if defined(_AIX) || (defined(__MVS__) && defined(__64BIT__))
+// AIX and 64-bit MVS must use _OptionalFill for ABI backward compatibility.
+  using _FillType = _OptionalFill<_Traits>;
+#else
+#if defined(_WIN32)
+  static const bool _OptOutForABICompat = true;
+#else
+  static const bool _OptOutForABICompat = false;
+#endif
+
+  using _FillType = _If<
+      sizeof(char_type) >= sizeof(int_type) && !_OptOutForABICompat,
+      _OptionalFill<_Traits>,
+      _SentinelValueFill<_Traits>
+  >;
+#endif
+  mutable _FillType __fill_;
 };
 
 template <class _CharT, class _Traits>
@@ -605,7 +648,7 @@ template <class _CharT, class _Traits>
 inline _LIBCPP_HIDE_FROM_ABI void basic_ios<_CharT, _Traits>::init(basic_streambuf<char_type, traits_type>* __sb) {
   ios_base::init(__sb);
   __tie_  = nullptr;
-  __fill_ = traits_type::eof();
+  __fill_ = widen(' ');
 }
 
 template <class _CharT, class _Traits>
@@ -655,16 +698,16 @@ inline _LIBCPP_HIDE_FROM_ABI _CharT basic_ios<_CharT, _Traits>::widen(char __c)
 
 template <class _CharT, class _Traits>
 inline _LIBCPP_HIDE_FROM_ABI _CharT basic_ios<_CharT, _Traits>::fill() const {
-  if (traits_type::eq_int_type(traits_type::eof(), __fill_))
+  if (!__fill_.__is_set())
     __fill_ = widen(' ');
-  return __fill_;
+  return __fill_.__fill();
 }
 
 template <class _CharT, class _Traits>
 inline _LIBCPP_HIDE_FROM_ABI _CharT basic_ios<_CharT, _Traits>::fill(char_type __ch) {
-  if (traits_type::eq_int_type(traits_type::eof(), __fill_))
+  if (!__fill_.__is_set())
     __fill_ = widen(' ');
-  char_type __r = __fill_;
+  char_type __r = __fill_.__fill();
   __fill_       = __ch;
   return __r;
 }
diff --git a/libcxx/test/std/input.output/iostream.format/std.manip/setfill_eof.pass.cpp b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_eof.pass.cpp
new file mode 100644
index 00000000000000..042e07cb80bbd0
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/std.manip/setfill_eof.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Test that weof as a wchar_t value can be set as the fill character.
+
+// UNSUPPORTED: no-wide-characters
+// REQUIRES: target=powerpc{{(64)?}}-ibm-aix || target=s390x-ibm-zos
+
+#include <iomanip>
+#include <ostream>
+#include <cassert>
+#include <string>
+
+template <class CharT>
+struct testbuf : public std::basic_streambuf<CharT> {
+  testbuf() {}
+};
+
+int main(int, char**) {
+  testbuf<wchar_t> sb;
+  std::wostream os(&sb);
+  os << std::setfill((wchar_t)std::char_traits<wchar_t>::eof());
+  assert(os.fill() == (wchar_t)std::char_traits<wchar_t>::eof());
+
+  return 0;
+}

@xingxue-ibm
Copy link
Contributor Author

@perry-ca

libcxx/include/ios Outdated Show resolved Hide resolved
…hanges are compatible with the current IBM provided libc++.
- update a comment
-define and use macros _LIBCXX_IOS_MAY_USE_OPTIONAL_FILL and _LIBCXX_IOS_FORCE_OPTIONAL_FILL.
@xingxue-ibm xingxue-ibm removed the openmp:libomp OpenMP host runtime label Jun 4, 2024
- expect the test case to pass except windows in abi version 1.
libcxx/include/ios Outdated Show resolved Hide resolved
libcxx/docs/ReleaseNotes/19.rst Outdated Show resolved Hide resolved
libcxx/docs/ReleaseNotes/19.rst Outdated Show resolved Hide resolved
libcxx/include/__configuration/abi.h Outdated Show resolved Hide resolved
- add note that the change only affect ABI version 2
- remove "undefining _LIBCXX_IOS_USE_FILL_HELPER to keep the ABI verson 1 behavior"
- add init() functions for class _FillHelper and _SentinelValueFill
@xingxue-ibm xingxue-ibm merged commit 194f98c into llvm:main Jul 17, 2024
57 checks passed
@xingxue-ibm
Copy link
Contributor Author

Thanks very much, @ldionne and @daltenty for your guidance and help to get the patch in the shape as it is now!

@vitalybuka
Copy link
Collaborator

daltenty added a commit that referenced this pull request Jul 18, 2024
….cpp

So we can also match aarch64 triples which have four components instead of three when disabling the test, which the case on some buildbots.

Follow on to #89305
@daltenty
Copy link
Member

Looks like this breaks: https://lab.llvm.org/buildbot/#/builders/85/builds/705 https://lab.llvm.org/buildbot/#/builders/24/builds/587

This test is already XFAIL'd for aarch64, but it seems the XFAIL in the test did not match on these bots because they have a four component triple in their test run.

I've update the XFAIL condition with ad15428, which should hopefully resolve the breakage.

@xingxue-ibm
Copy link
Contributor Author

Looks like this breaks: https://lab.llvm.org/buildbot/#/builders/85/builds/705 https://lab.llvm.org/buildbot/#/builders/24/builds/587

This test is already XFAIL'd for aarch64, but it seems the XFAIL in the test did not match on these bots because they have a four component triple in their test run.

I've update the XFAIL condition with ad15428, which should hopefully resolve the breakage.

Thanks. @daltenty!

@zeroomega
Copy link
Contributor

zeroomega commented Jul 18, 2024

Hi, we saw a series of weird test failures on our mac stage2 builder after this patch was landed. And after hours of bisecting and we confirmed it was caused by this patch. The error message we saw on our bot:

FAIL: LLVM :: tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test (48643 of 54789)
******************** TEST 'LLVM :: tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 21: /Volumes/Work/s/w/ir/x/w/llvm_build/tools/clang/stage2-bins/bin/llvm-debuginfo-analyzer --attribute=level,format                          --output-sort=offset                          --print=scopes,symbols,types,lines,instructions                          /Volumes/Work/s/w/ir/x/w/llvm-llvm-project/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-clang.o 2>&1 |  /Volumes/Work/s/w/ir/x/w/llvm_build/tools/clang/stage2-bins/bin/FileCheck --strict-whitespace -check-prefix=ONE /Volumes/Work/s/w/ir/x/w/llvm-llvm-project/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test
+ /Volumes/Work/s/w/ir/x/w/llvm_build/tools/clang/stage2-bins/bin/FileCheck --strict-whitespace -check-prefix=ONE /Volumes/Work/s/w/ir/x/w/llvm-llvm-project/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test
+ /Volumes/Work/s/w/ir/x/w/llvm_build/tools/clang/stage2-bins/bin/llvm-debuginfo-analyzer --attribute=level,format --output-sort=offset --print=scopes,symbols,types,lines,instructions /Volumes/Work/s/w/ir/x/w/llvm-llvm-project/llvm/test/tools/llvm-debuginfo-analyzer/COFF/Inputs/test-codeview-clang.o
/Volumes/Work/s/w/ir/x/w/llvm-llvm-project/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test:43:13: error: ONE-NEXT: expected string not found in input
; ONE-NEXT: [004]     5             {Line}
            ^
<stdin>:11:61: note: scanning from here
[004]                   {Variable} 'CONSTANT' -> 'const int'
                                                            ^
<stdin>:12:1: note: possible intended match here
[004] ����5             {Line}
^

Input file: <stdin>
Check file: /Volumes/Work/s/w/ir/x/w/llvm-llvm-project/llvm/test/tools/llvm-debuginfo-analyzer/COFF/01-coff-print-basic-details.test

-dump-input=help explains the following input dump.

Input was:
<<<<<<
           .
           .
           .
           6: [002]               {Function} extern not_inlined 'foo' -> 'int' 
           7: [003]                 {Parameter} 'ParamPtr' -> '* const int' 
           8: [003]                 {Parameter} 'ParamUnsigned' -> 'unsigned' 
           9: [003]                 {Parameter} 'ParamBool' -> 'bool' 
          10: [003]                 {Block} 
          11: [004]                   {Variable} 'CONSTANT' -> 'const int' 
next:43'0                                                                 X error: no match found
          12: [004] ����5             {Line} 
next:43'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
next:43'1     ?                               possible intended match
          13: [004]                   {Code} 'movl	$0x7, 0x4(%rsp)' 
next:43'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          14: [004] ����6             {Line} 
next:43'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          15: [004]                   {Code} 'movl	$0x7, 0x1c(%rsp)' 
next:43'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          16: [004]                   {Code} 'jmp	0x8' 
next:43'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          17: [003]                 {TypeAlias} 'INTEGER' -> 'int' 
next:43'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           .
           .
           .
>>>>>>

--

********************

You can see that the clang emitted some non-readable characters in this unit test. These characters were not emitted before this PR was landed.

Our mac stage2 toolchain was linked to the stage1 toolchain's libcxx, that probably explained why the stage1 builder was green on this PR but the test failed on the stage2 build, since the stage1 toolchain used host toolchain's libcxx, which doesn't have this PR. The issue only happens on Mac. It looks like Windows and Linux, are unaffected.
Example bot run with this issue: https://ci.chromium.org/ui/p/fuchsia/builders/prod/clang-mac-x64/b8742128214337927377/overview

Could you take a look please? If it takes a while to fix, could you revert this change and land it later?

@ldionne
Copy link
Member

ldionne commented Jul 18, 2024

@xingxue-ibm The most likely thing here is that this patch changed the size, alignment, trailing padding or some triviality property of basic_ios when the ABI flag is disabled. Maybe the _LIBCPP_PACKED attribute is to blame? Either way, if we can't figure it out quickly, we should probably revert this because we are close to the LLVM 19 release and we want the tree to be as stable as possible.

Also, once we figure it out, we should add a test for that.

@daltenty
Copy link
Member

An initial thing note is this is this buildbot seems to be explicit building the version 2 ABI, not the stable one:

set(LIBCXX_ABI_VERSION 2 CACHE STRING "")

I'll continue investigation

@daltenty
Copy link
Member

daltenty commented Jul 19, 2024

@zeroomega we’ve taken a look at the buildbot config and outputs.

From what we can see your bots stage 1 use a clang with a custom v2 libc++ from cpid:

 ‘-DCMAKE_C_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang’,
 ‘-DCMAKE_CXX_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang++‘,
 ‘-DCMAKE_ASM_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang’,
…
 ‘-DCMAKE_SHARED_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,
 ‘-DCMAKE_MODULE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,
 ‘-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,

https://logs.chromium.org/logs/fuchsia/buildbucket/cr-buildbucket/8742128214337927377/+/u/clang/configure/execution_details

And then we have some more cmake flags for stage2 which seem to be set to link the cpid library, not the just built one in the build tree:

 ‘-DSTAGE2_CROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_C_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang;-DCMAKE_CXX_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang++;-DCMAKE_OSX_SYSROOT=/Volumes/Work/s/w/ir/cache/macos_sdk/XCode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk;-DCMAKE_SHARED_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a;-DCMAKE_MODULE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a;-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,

Our current theory based on this is your stage2 built clang is loading the cpid shipped libc++ instead of the just-built libc++ from stage 1.

That could explain the problems your are seeing, since we changed the offset of the fill character in the layout of basic_ios in the unstable v2 ABI with this patch.

Are you able to try running the tests with a custom DYLD_LIBRARY_PATH pointed at the builds lib directory (the one containing the just-built libc++) to verify if that resolves the test failure? It would be good to check the rpath, etc on the stage2 built clang as well to confirm what version of libc++ is actually being loaded.

(You can reach me as daltenty on LLVM discord if you’d like to discuss further. I've been trying to replicate the full bot config locally on an x64 mac but there's a lot of moving parts in that bot.)

@zeroomega
Copy link
Contributor

zeroomega commented Jul 19, 2024

@zeroomega we’ve taken a look at the buildbot config and outputs.

From what we can see your bots stage 1 use a clang with a custom v2 libc++ from cpid:

 ‘-DCMAKE_C_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang’,
 ‘-DCMAKE_CXX_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang++‘,
 ‘-DCMAKE_ASM_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang’,
…
 ‘-DCMAKE_SHARED_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,
 ‘-DCMAKE_MODULE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,
 ‘-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,

https://logs.chromium.org/logs/fuchsia/buildbucket/cr-buildbucket/8742128214337927377/+/u/clang/configure/execution_details

And then we have some more cmake flags for stage2 which seem to be set to link the cpid library, not the just built one in the build tree:

 ‘-DSTAGE2_CROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_C_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang;-DCMAKE_CXX_COMPILER=/Volumes/Work/s/w/ir/x/w/cipd/bin/clang++;-DCMAKE_OSX_SYSROOT=/Volumes/Work/s/w/ir/cache/macos_sdk/XCode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk;-DCMAKE_SHARED_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a;-DCMAKE_MODULE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a;-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++ /Volumes/Work/s/w/ir/x/w/cipd/lib/libc++.a’,

Our current theory based on this is your stage2 built clang is loading the cpid shipped libc++ instead of the just-built libc++ from stage 1.

That could explain the problems your are seeing, since we changed the offset of the fill character in the layout of basic_ios in the unstable v2 ABI with this patch.

Are you able to try running the tests with a custom DYLD_LIBRARY_PATH pointed at the builds lib directory (the one containing the just-built libc++) to verify if that resolves the test failure? It would be good to check the rpath, etc on the stage2 built clang as well to confirm what version of libc++ is actually being loaded.

(You can reach me as daltenty on LLVM discord if you’d like to discuss further. I've been trying to replicate the full bot config locally on an x64 mac but there's a lot of moving parts in that bot.)

I don't think your theory is correct, as the CROSS_NATIVE flag is only going to be effective when doing cross compilation. We do have a cross compile mac-arm64 builder that why those flags were there (we should clean them up when doing native host build). In addition, when I was bisecting locally, I omit the CROSS_NATIVE flags. The flag set i used are:

/Users/haowei/SRC/llvm-prebuilts/cmake/mac-amd64/bin/cmake \
  -S \
  /Users/haowei/SRC/llvm-project/llvm \
  -GNinja \
  -DCMAKE_MAKE_PROGRAM=/Users/haowei/SRC/llvm-prebuilts/ninja/mac-amd64/ninja \
  -DCMAKE_INSTALL_PREFIX= \
  -DCMAKE_C_COMPILER=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/clang \
  -DCMAKE_CXX_COMPILER=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/clang++ \
  -DCMAKE_ASM_COMPILER=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/clang \
  -DCLANG_REPOSITORY_STRING=https://llvm.googlesource.com/llvm-project \
  -DCMAKE_LIBTOOL=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/llvm-libtool-darwin \
  -DCMAKE_LIPO=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/llvm-lipo \
  -DCMAKE_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.sdk \
  -DLLVM_ENABLE_ZLIB=FORCE_ON \
  -DZLIB_INCLUDE_DIR=/Users/haowei/SRC/llvm-prebuilts/reproduce/zlib_install_target/include \
  -DZLIB_LIBRARY=/Users/haowei/SRC/llvm-prebuilts/reproduce/zlib_install_target/lib/libz.a \
  -DLLVM_ENABLE_ZSTD=FORCE_ON \
  -Dzstd_DIR=/Users/haowei/SRC/llvm-prebuilts/reproduce/zstd_install/lib/cmake/zstd \
  -DLLVM_ENABLE_LIBXML2=FORCE_ON \
  -DLibXml2_ROOT=/Users/haowei/SRC/llvm-prebuilts/reproduce/libxml2_install_target/lib/cmake/libxml2-2.9.10 \
  -DLLVM_ENABLE_CURL=FORCE_ON \
  -DCURL_ROOT=/Users/haowei/SRC/llvm-prebuilts/reproduce/curl_install/lib/cmake/CURL \
  -DOpenSSL_ROOT=/Users/haowei/SRC/llvm-prebuilts/reproduce/boringssl_install/lib/cmake/OpenSSL \
  -DLLVM_ENABLE_HTTPLIB=FORCE_ON \
  -Dhttplib_ROOT=/Users/haowei/SRC/llvm-prebuilts/reproduce/cpp_httplib_install/lib/cmake/httplib \
  -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON \
  "-DCMAKE_SHARED_LINKER_FLAGS=-nostdlib++ /Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a" \
  "-DCMAKE_MODULE_LINKER_FLAGS=-nostdlib++ /Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a" \
  "-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++ /Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a" \
  -DSTAGE2_CMAKE_LINKER=/usr/bin/ld \
  -DSTAGE2_LINUX_aarch64-unknown-linux-gnu_SYSROOT=/Users/haowei/SRC/llvm-prebuilts/sysroot/linux \
  -DSTAGE2_LINUX_armv7-unknown-linux-gnueabihf_SYSROOT=/Users/haowei/SRC/llvm-prebuilts/sysroot/linux \
  -DSTAGE2_LINUX_i386-unknown-linux-gnu_SYSROOT=/Users/haowei/SRC/llvm-prebuilts/sysroot/linux \
  -DSTAGE2_LINUX_riscv64-unknown-linux-gnu_SYSROOT=/Users/haowei/SRC/llvm-prebuilts/sysroot/focal \
  -DSTAGE2_LINUX_x86_64-unknown-linux-gnu_SYSROOT=/Users/haowei/SRC/llvm-prebuilts/sysroot/linux \
  -DSTAGE2_FUCHSIA_SDK=/Users/haowei/SRC/llvm-prebuilts/sdk \
  "-DSTAGE2_LLVM_LIT_ARGS=--resultdb-output=r.j -v" \
  -DSTAGE2_LLVM_ENABLE_LTO=OFF \
  -DSTAGE2_LLVM_RAEVICT_MODEL_PATH=none \
  -DCURSES_INCLUDE_DIRS=/Users/haowei/SRC/llvm-prebuilts/reproduce/ncurses_install/include\;/Users/haowei/SRC/llvm-prebuilts/reproduce/ncurses_install/include/ncurses \
  -DCURSES_LIBRARIES=/Users/haowei/SRC/llvm-prebuilts/reproduce/ncurses_install/lib/libncurses.a \
  -DPANEL_LIBRARIES=/Users/haowei/SRC/llvm-prebuilts/reproduce/ncurses_install/lib/libpanel.a \
  -DLLVM_ENABLE_TERMINFO=FORCE_ON \
  -DTerminfo_LIBRARIES=/Users/haowei/SRC/llvm-prebuilts/reproduce/ncurses_install/lib/libncurses.a \
  -DLLVM_ENABLE_LIBEDIT=FORCE_ON \
  -DLibEdit_INCLUDE_DIRS=/Users/haowei/SRC/llvm-prebuilts/reproduce/libedit_install/include \
  -DLibEdit_LIBRARIES=/Users/haowei/SRC/llvm-prebuilts/reproduce/libedit_install/lib/libedit.a\;/Users/haowei/SRC/llvm-prebuilts/reproduce/ncurses_install/lib/libncurses.a \
  -C ../clang/cmake/caches/Fuchsia.cmake

So I don't think the issue is stage2 clang is loading stage1 libcxx. In addition, we always do static linking, I am not sure if DYLD_LIBRARY_PATH is going to be effective in this case but I will have a try when I am back to my workstation.
C.C. @petrhosek

@daltenty
Copy link
Member

So I don't think the issue is stage2 clang is loading stage1 libcxx. In addition, we always do static linking, I am not sure if DYLD_LIBRARY_PATH is going to be effective in this case but I will have a try when I am back to my workstation.

Thanks for the additional information. That's a little mysterious then, the issue your seeing certainly seems to resemble a layout mismatch on basic_ios, but the only obvious way to end up with such a case is if the definition in the library being used and the definition in the headers disagree (i.e. using a version before the change in one place and the one after in the other).

I'll continue to try to reproduce locally with the information you've provided as well.

@zeroomega
Copy link
Contributor

zeroomega commented Jul 19, 2024

@daltenty

I further remove unnecessary cmake flags and it still produce the same error.

/Users/haowei/SRC/llvm-prebuilts/cmake/mac-amd64/bin/cmake \
  -S \
  /Users/haowei/SRC/llvm-project/llvm \
  -GNinja \
  -DCMAKE_MAKE_PROGRAM=/Users/haowei/SRC/llvm-prebuilts/ninja/mac-amd64/ninja \
  -DCMAKE_INSTALL_PREFIX= \
  -DCMAKE_C_COMPILER=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/clang \
  -DCMAKE_CXX_COMPILER=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/clang++ \
  -DCMAKE_ASM_COMPILER=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/clang \
  -DCLANG_REPOSITORY_STRING=https://llvm.googlesource.com/llvm-project \
  -DCMAKE_LIBTOOL=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/llvm-libtool-darwin \
  -DCMAKE_LIPO=/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/bin/llvm-lipo \
  -DCMAKE_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.sdk \
  -DCMAKE_FIND_PACKAGE_PREFER_CONFIG=ON \
  "-DCMAKE_SHARED_LINKER_FLAGS=-nostdlib++ /Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a" \
  "-DCMAKE_MODULE_LINKER_FLAGS=-nostdlib++ /Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a" \
  "-DCMAKE_EXE_LINKER_FLAGS=-nostdlib++ /Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a" \
  "-DSTAGE2_LLVM_LIT_ARGS=--resultdb-output=r.j -v" \
  -DSTAGE2_LLVM_ENABLE_LTO=OFF \
  -DSTAGE2_LLVM_RAEVICT_MODEL_PATH=none \
  -DLLVM_ENABLE_LIBEDIT=FORCE_ON \
  -C ../clang/cmake/caches/Fuchsia.cmake

The failure can be triggered by

LIT_FILTER="01-coff-print-basic-details.test" ninja stage2-check-llvm

You can get our host clang toolchain from https://chrome-infra-packages.appspot.com/p/fuchsia/third_party/clang/mac-amd64/+/integration and you just need cmake and ninja and xcode to reproduce the build.

I will try to see if I can get a more concrete confirmation that the libc++ for the stage2 build is coming from stage1 toolchain build.

@zeroomega
Copy link
Contributor

@daltenty Thanks for looking into this. At this point I am 90% confident that the test failure on 01-coff-print-basic-details.test is an issue on our side.
I did a local build and rerun the stage2 build of llvm-debuginfo-analyzer (which is what was failing in the unit test) with -v and noticed that the stage 1's libc++ linker flag (/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a) was incorrectly passed through:

_  stage2-bins git:(bad) _ ninja llvm-debuginfo-analyzer -v
[1/1] : && /Users/haowei/SRC/llvm-project/build_stage2/./bin/clang++ --sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.sdk -stdlib=libc++ -fPIC -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffile-prefix-map=/Users/haowei/SRC/llvm-project/build_stage2/tools/clang/stage2-bins=../../../../ -ffile-prefix-map=/Users/haowei/SRC/llvm-project/= -no-canonical-prefixes -O3 -DNDEBUG -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.4.sdk -mmacosx-version-min=10.13 -Wl,-search_paths_first -Wl,-headerpad_max_install_names -nostdlib++ /Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a -stdlib=libc++ -static-libstdc++ -fuse-ld=lld -Wl,--color-diagnostics -Wl,-lto_library -Wl,/Users/haowei/SRC/llvm-project/build_stage2/./lib/libLTO.dylib    -Wl,-dead_strip tools/llvm-debuginfo-analyzer/CMakeFiles/llvm-debuginfo-analyzer.dir/llvm-debuginfo-analyzer.cpp.o tools/llvm-debuginfo-analyzer/CMakeFiles/llvm-debuginfo-analyzer.dir/Options.cpp.o -o bin/llvm-debuginfo-analyzer  -Wl,-rpath,@loader_path/../lib  lib/libLLVMX86Desc.a  lib/libLLVMARMDesc.a  lib/libLLVMAArch64Desc.a  lib/libLLVMRISCVDesc.a  lib/libLLVMX86Disassembler.a  lib/libLLVMARMDisassembler.a  lib/libLLVMAArch64Disassembler.a  lib/libLLVMRISCVDisassembler.a  lib/libLLVMX86Info.a  lib/libLLVMARMInfo.a  lib/libLLVMAArch64Info.a  lib/libLLVMRISCVInfo.a  lib/libLLVMBinaryFormat.a  lib/libLLVMDebugInfoCodeView.a  lib/libLLVMDebugInfoLogicalView.a  lib/libLLVMDebugInfoPDB.a  lib/libLLVMMC.a  lib/libLLVMMCDisassembler.a  lib/libLLVMObject.a  lib/libLLVMSupport.a  lib/libLLVMARMDesc.a  lib/libLLVMARMInfo.a  lib/libLLVMARMUtils.a  lib/libLLVMAArch64Desc.a  lib/libLLVMCodeGenTypes.a  lib/libLLVMAArch64Info.a  lib/libLLVMAArch64Utils.a  lib/libLLVMRISCVDesc.a  lib/libLLVMRISCVInfo.a  lib/libLLVMMCDisassembler.a  lib/libLLVMDebugInfoMSF.a  lib/libLLVMDebugInfoDWARF.a  lib/libLLVMObject.a  lib/libLLVMIRReader.a  lib/libLLVMBitReader.a  lib/libLLVMAsmParser.a  lib/libLLVMCore.a  lib/libLLVMRemarks.a  lib/libLLVMBitstreamReader.a  lib/libLLVMMCParser.a  lib/libLLVMMC.a  lib/libLLVMDebugInfoCodeView.a  lib/libLLVMTextAPI.a  lib/libLLVMBinaryFormat.a  lib/libLLVMTargetParser.a  lib/libLLVMSupport.a  lib/libLLVMDemangle.a  -lm && :
clang++: warning: argument unused during compilation: '-stdlib=libc++' [-Wunused-command-line-argument]
clang++: warning: argument unused during compilation: '-stdlib=libc++' [-Wunused-command-line-argument]
clang++: warning: argument unused during compilation: '-static-libstdc++' [-Wunused-command-line-argument]

So the stage2 clang, when building this binary was linked to the host's libcxx. I will try to manually correct it to the stage2 libcxx to see if the failure disappear.

@daltenty
Copy link
Member

@daltenty Thanks for looking into this. At this point I am 90% confident that the test failure on 01-coff-print-basic-details.test is an issue on our side. I did a local build and rerun the stage2 build of llvm-debuginfo-analyzer (which is what was failing in the unit test) with -v and noticed that the stage 1's libc++ linker flag (/Users/haowei/SRC/llvm-prebuilts/clang/mac-amd64/lib/libc++.a) was incorrectly passed through:

...

So the stage2 clang, when building this binary was linked to the host's libcxx. I will try to manually correct it to the stage2 libcxx to see if the failure disappear.

No problem, thanks for confirming (my local build is still running). That would definitely explain the issues you are seeing, since the two libraries disagree on the layout.
(I haven't seen a lot of buildbots using this -DSTAGE2_ mechanism so potentially there could be some CMake bugs lurking in that area)

Harini0924 pushed a commit to Harini0924/llvm-project that referenced this pull request Jul 22, 2024
….cpp

So we can also match aarch64 triples which have four components instead of three when disabling the test, which the case on some buildbots.

Follow on to llvm#89305
sgundapa pushed a commit to sgundapa/upstream_effort that referenced this pull request Jul 23, 2024
…vm#89305)

`libcxx std::basic_ios` uses `WEOF` to indicate the `fill` value is
uninitialized. On some platforms (e.g AIX and zOS in 64-bit mode)
`wchar_t` is 4 bytes `unsigned` and `wint_t` is also 4 bytes which means
`WEOF` cannot be distinguished from `WCHAR_MAX` by
`std::char_traits<wchar_t>::eq_int_type()`, meaning this valid character
value cannot be stored on affected platforms (as the implementation
triggers reinitialization to `widen(’ ’)`).

This patch introduces a new helper class `_FillHelper` uses a boolean
variable to indicate whether the fill character has been initialized,
which is used by default in libcxx ABI version 2. The patch does not
affect ABI version 1 except for targets AIX in 32- and 64-bit and z/OS
in 64-bit (so that the layout of the implementation is compatible with
the current IBM system provided libc++)

This is a continuation of Phabricator patch
[D124555](https://reviews.llvm.org/D124555). This patch uses a modified
version of the [approach](https://reviews.llvm.org/D124555#3566746)
suggested by @ldionne .

---------

Co-authored-by: Louis Dionne <[email protected]>
Co-authored-by: David Tenty <[email protected]>
sgundapa pushed a commit to sgundapa/upstream_effort that referenced this pull request Jul 23, 2024
….cpp

So we can also match aarch64 triples which have four components instead of three when disabling the test, which the case on some buildbots.

Follow on to llvm#89305
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
…9305)

Summary:
`libcxx std::basic_ios` uses `WEOF` to indicate the `fill` value is
uninitialized. On some platforms (e.g AIX and zOS in 64-bit mode)
`wchar_t` is 4 bytes `unsigned` and `wint_t` is also 4 bytes which means
`WEOF` cannot be distinguished from `WCHAR_MAX` by
`std::char_traits<wchar_t>::eq_int_type()`, meaning this valid character
value cannot be stored on affected platforms (as the implementation
triggers reinitialization to `widen(’ ’)`).

This patch introduces a new helper class `_FillHelper` uses a boolean
variable to indicate whether the fill character has been initialized,
which is used by default in libcxx ABI version 2. The patch does not
affect ABI version 1 except for targets AIX in 32- and 64-bit and z/OS
in 64-bit (so that the layout of the implementation is compatible with
the current IBM system provided libc++)

This is a continuation of Phabricator patch
[D124555](https://reviews.llvm.org/D124555). This patch uses a modified
version of the [approach](https://reviews.llvm.org/D124555#3566746)
suggested by @ldionne .

---------

Co-authored-by: Louis Dionne <[email protected]>
Co-authored-by: David Tenty <[email protected]>

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 


Differential Revision: https://phabricator.intern.facebook.com/D60250925
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
….cpp

Summary:
So we can also match aarch64 triples which have four components instead of three when disabling the test, which the case on some buildbots.

Follow on to #89305

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 


Differential Revision: https://phabricator.intern.facebook.com/D60250935
daltenty added a commit that referenced this pull request Sep 30, 2024
This is a small fix to #89305.
In the `__init` function of `_FillHelper`, `__fill_val_` was left
uninitialized. This worked for the implementation in the PR because we
always checked `__set_` before trying to read it, and would initialize
if it was unset.

However it turns out in earlier versions of the header (at least on AIX
which followed this path), we do a read of `__fill_val_` even if
`__set_` was false before initializing, to check if it matched the
sentinel value, so this causes undesired behaviour and UB.
puja2196 pushed a commit to puja2196/LLVM-tutorial that referenced this pull request Oct 2, 2024
This is a small fix to llvm/llvm-project#89305.
In the `__init` function of `_FillHelper`, `__fill_val_` was left
uninitialized. This worked for the implementation in the PR because we
always checked `__set_` before trying to read it, and would initialize
if it was unset.

However it turns out in earlier versions of the header (at least on AIX
which followed this path), we do a read of `__fill_val_` even if
`__set_` was false before initializing, to check if it matched the
sentinel value, so this causes undesired behaviour and UB.
VitaNuo pushed a commit to VitaNuo/llvm-project that referenced this pull request Oct 2, 2024
This is a small fix to llvm#89305.
In the `__init` function of `_FillHelper`, `__fill_val_` was left
uninitialized. This worked for the implementation in the PR because we
always checked `__set_` before trying to read it, and would initialize
if it was unset.

However it turns out in earlier versions of the header (at least on AIX
which followed this path), we do a read of `__fill_val_` even if
`__set_` was false before initializing, to check if it matched the
sentinel value, so this causes undesired behaviour and UB.
VitaNuo pushed a commit to VitaNuo/llvm-project that referenced this pull request Oct 2, 2024
This is a small fix to llvm#89305.
In the `__init` function of `_FillHelper`, `__fill_val_` was left
uninitialized. This worked for the implementation in the PR because we
always checked `__set_` before trying to read it, and would initialize
if it was unset.

However it turns out in earlier versions of the header (at least on AIX
which followed this path), we do a read of `__fill_val_` even if
`__set_` was false before initializing, to check if it matched the
sentinel value, so this causes undesired behaviour and UB.
Sterling-Augustine pushed a commit to Sterling-Augustine/llvm-project that referenced this pull request Oct 3, 2024
This is a small fix to llvm#89305.
In the `__init` function of `_FillHelper`, `__fill_val_` was left
uninitialized. This worked for the implementation in the PR because we
always checked `__set_` before trying to read it, and would initialize
if it was unset.

However it turns out in earlier versions of the header (at least on AIX
which followed this path), we do a read of `__fill_val_` even if
`__set_` was false before initializing, to check if it matched the
sentinel value, so this causes undesired behaviour and UB.
xgupta pushed a commit to xgupta/llvm-project that referenced this pull request Oct 4, 2024
This is a small fix to llvm#89305.
In the `__init` function of `_FillHelper`, `__fill_val_` was left
uninitialized. This worked for the implementation in the PR because we
always checked `__set_` before trying to read it, and would initialize
if it was unset.

However it turns out in earlier versions of the header (at least on AIX
which followed this path), we do a read of `__fill_val_` even if
`__set_` was false before initializing, to check if it matched the
sentinel value, so this causes undesired behaviour and UB.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants