On Wednesday 21 September 2022 11:31:54 Martin Storsjö wrote:
> On Tue, 20 Sep 2022, Pali Rohár wrote:
> 
> > > There are many, many different variants of inline behaviour - C89 inline,
> > > C99 inline, static inline, extern inline, gnu_inline, etc. There's a 
> > > couple
> > > different styles used here; all the inline functions that we're talking
> > > about here have static inline linkage - which doesn't require any external
> > > linkage version of the function.
> > > 
> > > When dealing with the other ones that do require external linkage to be
> > > present, there's a whole rat's nest of potential issues, since sometimes 
> > > one
> > > compiler never tries to use the external version, but other compilers do,
> > > etc.
> 
> FWIW a few more side notes about inline functions; the issue is that they
> need to work in whichever mode (C89, C99, or C++) that the user chooses to
> build with. In practice, this has boiled down to essentially two choices;
> "extern inline __attribute__((gnu_inline))" for inlines where we do have an
> extern definition somewhere (also, see __CRT_INLINE in _mingw.h), and plain
> "static inline" for cases where we don't have an extern definition.
> 
> The static inline case has got the disadvantage that if you take the address
> of it, you get a different address in each translation unit that refers to
> it. (The C++ style inline, which gets comdat folded across object files that
> have instantiated it, is nicer in this aspect, but isn't available equally
> in all language modes.)
> 
> But this is any case beside the point here; if we should change these
> things, this should be the sole topic of the patches.

I would propose to use extern inline for functions which already have
some non-inline wrapper in mingw-w64 code. So getting pointer to such
function would result in the same address.

> > Hello! Thank you very much for all those details. I did not investigated
> > everything yet, just small subset. But runtime I mean all of them which
> > you mentioned (from mingw-w64-crt to libgcc/stdc++...). Public
> > structures and dll libs are problematic. I was planning to look at it
> > what is different, how and what can be done to achieve binary
> > compatibility.
> > 
> > The main issue which I see is that if you "compile" source code (gcc -c)
> > of some application then it already reference ucrt symbols (if mingw-w64
> > runtime was compiled for ucrt) because header files automatically inline
> > some ucrt functions. Same for msvcrt.
> > 
> > I see that mingw-w64 crt is compiled with "-D__MSVCRT_VERSION__=0x700"
> > which you mentioned, and which should "workaround" above issue.
> > 
> > So if this issue can be "fixed" then building static libraries can be
> > CRT independent.
> 
> It can at least currently be fixed, for object files that don't use any
> exotic features. Things like libgcc (in a statically linked form) most
> probably can easily be made compatible with both, if it isn't already (I
> would kinda expect it to).
> 
> I believe libstdc++ uses a much wider area of the CRT though, at least I
> know that libc++ uses lots of the locale functions - so those might be
> harder.
> 
> Such a plan to gradually reduce the object file level differences between
> UCRT and msvcr* cases, to ease interoperability, sounds reasonble to me. I
> won't mind patches to move _snprintf and _scprintf to extern definitions
> just like e.g. printf today - but just for clarity, remove the inlines in
> the headers in that case, so they're consistent with the rest. And make the
> reasoning clear in the commit message, that you're doing it with the intent
> to reduce object file level differences between UCRT and msvcrt.

I think that for fully-optimized code it is a good idea to have all
those inline definitions, so compiler can reduce usage of wrappers from
static library. But it requires to generate code which is ucrt or msvcrt
dependent and not crt-agnostic.

So what about having both variants? (extern) inline definition and also
wrapper in static library for crt-agnostic version?

> As an aid in making object files that work with environments, maybe it would
> be useful with e.g. a define like __CRT_AGNOSTIC__ (or a better name), which
> would hide all the details that differ (e.g. the contents of struct
> threadlocinfo, and _iobuf) and all inline functions.

This sounds like a good idea. With that new define (__CRT_AGNOSTIC__ or
any other) it can hide inline definitions and forces compiler to use
external function from static/import library (based on chosen crt dll at
the link time).

> For _iobuf, there's
> very few applications out there that touch it, but threadlocinfo is used by
> ctype.h.
> 
> For ctype.h, I'm not sure if it's possible to avoid the issue by making
> _ischartype_l an extern function instead of an inline macro - but that
> certainly would have a performance impact.
> 
> Another CRT portability issue which is less obvious, is that e.g. the printf
> functions use different format attributes (ms_printf vs gnu_printf), and
> similarly, the format defines like PRId64 expand to %lld vs %I64d. Then
> again, while %lld is preferred for UCRT, I would expect that it does handle
> the legacy form %I64d too, so for __CRT_AGNOSTIC__, I guess it could use the
> older forms.
> 
> // Martin

Format attributes is interesting issue. Because there are already following
printf implementations:

1) C89 (without %lld) (crtdll.dll, msvcrt10.dll)
2) C89 (without %lld) + MS extensions %I64d (msvcrt20.dll - msvcr110.dll)
3) C99 (with %lld) + MS extensions %I64d (msvcr120.dll, vcruntime140.dll)
4) C99 (with %lld) + GNU extensions (this is what glibc has)
5) C99 (with %lld) + MS extensions + GNU extensions (mingw-w64 pformat.c)

And probably somebody can write (or maybe it already exists on internet)
also some clean C99 implementations without any MS or GNU extensions.

But gcc provides only ms_printf and gnu_printf format attributes which I
guess matches 2) and 4).

Simple compile test (without linking) which proves my guess:

  $ cat test.c
  int printf_ms(const char *format, ...) __attribute__((format(ms_printf, 1, 
2)));
  int printf_gnu(const char *format, ...) __attribute__((format(gnu_printf, 1, 
2)));
  void test(long long a, long long b) {
    printf_ms("%lld %I64d", a, b);
    printf_gnu("%lld %I64d", a, b);
  }

  $ i686-w64-mingw32-gcc -Wformat -c test.c
  test7.c: In function ‘test’:
  test7.c:4:16: warning: unknown conversion type character ‘l’ in format 
[-Wformat=]
     printf_ms("%lld %I64d", a, b);
                  ^
  test7.c:4:13: warning: too many arguments for format [-Wformat-extra-args]
     printf_ms("%lld %I64d", a, b);
               ^~~~~~~~~~~~
  test7.c:5:24: warning: format ‘%d’ expects argument of type ‘int’, but 
argument 3 has type ‘long long int’ [-Wformat=]
     printf_gnu("%lld %I64d", a, b);
                      ~~~~^      ~
                      %I64lld

So none of these attributes can be used for mingw-w64 pformat.c
implementation which supports both %lld and %I64d.

So issue is already there even without crt agnostic version.


_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to