Skip to content

Conversation

cmgauger
Copy link
Contributor

Per Microsoft documentation versions Visual Studio 2013 and below do not support the hh, j, z, and t length prefixes for printf format-type specifiers. Further, the preprocessor selection logic was modified so that 64-bit Visual Studio versions are actually recognized.

Per Microsoft documentation versions Visual Studio 2013 and below do not support
the hh, j, z, and t length prefixes for printf format-type specifiers. Further,
the preprocessor selection logic was modified so that 64-bit Visual Studio
versions are actually recognized.
@cmgauger
Copy link
Contributor Author

Just an aside comment outside of the actual commit message:

I actually found the error because attempting to mount the IBM 1401 software kit tapes (e.g. AUTOCODER, FORTRAN, and the diagnostics tape) resulted in SIMH bailing when trying to print the "%SIM-INFO: contains bytes of tape data ( records, tapemarks)" message, but not when SIMH was set to quiet mode.

Tracing the error showed the problem was the %zu printf format-type specifier -- in Visual Studio 2010.

@bscottm
Copy link
Contributor

bscottm commented Jul 24, 2025

@cmgauger: Why the ancient compiler? The de facto C dialect is C99, if you believe the makefile.

@bscottm
Copy link
Contributor

bscottm commented Jul 24, 2025

@cmgauger : Need to rethink this PR a bit so that it uses _MSC_VER more effectively because MS has become more standard compliant so that the "z" qualifier for size_t is highly preferred over "I32" and "I64". This patch would break MinGW builds because the MinGW C runtime is standard compliant and uses the "z" qualifier. MinGW's GCC -Wformat will complain loudly about "I32" and "I64".

Need to research when MS introduced the "z" qualifier and conditionalize the preprocessor statements accordingly, i.e., _MSC_VER <= <VS version number here>.

@cmgauger
Copy link
Contributor Author

It appears that VS 2015 is when it actually became standards compliant.

In any case, Microsoft's documentation from VS 2015 up to VS 2022 shows that z or I may be used for ptrdiff_t and size_t and j or I may be used for intmax_t/uintmax_t; further Microsoft prefers the use of I for __int32 and __int64 (and unsigned versions thereof):

The ptrdiff_t and size_t types are __int32 or unsigned __int32 on 32-bit platforms, and __int64 or unsigned __int64 on 64-bit platforms. The I (uppercase i), j, t, and z size prefixes take the correct argument width for the platform.

The documentation has that exact same paragraph even in VS 2022 documentation. Given that then, it seems that builds on VS should use I32 or I64 (as appropriate) instead of z, even on versions of VS that support the z size prefix.

This patch would break MinGW builds because the MinGW C runtime is standard compliant and uses the "z" qualifier. MinGW's GCC -Wformat will complain loudly about "I32" and "I64".

Which is funny, because the original, unmodified, set of conditionals still defaults to using I32 and I64 for uint64_t and int64_t formats on both VS and MinGW:

#if defined (_WIN32) || defined(_WIN64)

#  if defined(__MINGW64__)
#    define LL_FMT     "I64"
#    define SIZE_T_FMT "I64"
#  elif defined(_MSC_VER) || defined(__MINGW32__)
#    define LL_FMT     "ll"
#    define SIZE_T_FMT "z"
#  else
     /* Graceful fail -- shouldn't ever default to this on a Windows platform. */
#    define LL_FMT     "ll"
#    define SIZE_T_FMT "I32"
#  endif

#  define T_UINT64_FMT   "I64"
#  define T_INT64_FMT    "I64"
#  define POINTER_FMT    "p"

So if we have to have MinGW use standards compliant size prefixes instead of Microsoft's own, it'll need to be changed to something like:

#if defined (_WIN32) || defined(_WIN64)

#  if defined(__MINGW64__) || defined(__MINGW32__)
     /* MinGW, 32- or 64- bit: use standards compliant prefixes */
#    define SIZE_T_FMT   "z"
#    define T_UINT64_FMT "j"
#    define T_INT64_FMT  "j"
#  elif defined (_WIN64)
     /* VS, 64-bit: use Microsoft's preferred I prefix */
#    define SIZE_T_FMT   "I64"
#    define T_UINT64_FMT "I64"
#    define T_INT64_FMT  "I64"
#  else
     /* VS, 32-bit: use Microsoft's preferred I prefix */
#    define SIZE_T_FMT   "I32"
#    define T_UINT64_FMT "I64"
#    define T_INT64_FMT  "I64"
#  endif

#  define LL_FMT         "ll"
#  define POINTER_FMT    "p"

I moved the long long prefix out of the conditional, since even the pre-standards compliant versions of VS suggest you use ll for long long, and I moved the int64_t/uint64_t formats into the conditional given Microsoft's preference to I64 (and how on MinGW we can use j).

If that's good, I'll push a replacement.

@bscottm
Copy link
Contributor

bscottm commented Jul 25, 2025

The documentation has that exact same paragraph even in VS 2022 documentation. Given that then, it seems that builds on VS should use I32 or I64 (as appropriate) instead of z, even on versions of VS that support the z size prefix.

With the "z" prefix, whether printf interprets size_t as 32 or 64 bits depends solely on the C runtime. So you don't have to worry about whether it's 32 or 64 bits and the "I32" or"I64" prefixes, as is the general point of the standard. No real need to propagate MS weirdness any farther than necessary.

Which is funny, because the original, unmodified, set of conditionals still defaults to using I32 and I64 for uint64_t and int64_t formats on both VS and MinGW:

Yes, I know. I've been meaning to fix that. But with 19 PRs in the queue, it could be a while. (No, I don't have commit privs and that's probably a good idea because having a second set of eyes reviewing code is a good thing.)

So if we have to have MinGW use standards compliant size prefixes instead of Microsoft's own, it'll need to be changed to something like:

I'd suggest using the "z" prefix for post-VS2015, "I32"/"I64" for VS-2015 and earlier. Don't worry about MinGW -- I'll fix that after this PR is merged.

N.B.: MS is slowly converging on Clang, it would seem, with respect to C11 and C23 standard support. MS Visual Studio has been using ninja and clang to support CMake-based projects for a while (since 2019, IIRC, definitely in 2022.) So, coding to the standard when it can be used is preferable.

N.N.B.: SIMH cannot be compiled with Clang on Windows due to interesting coding choices in the SLiRP support code. The SLiRP replacement, libslirp, fixes the issues although you're going to be affected because libslirp makes use of C99-isms, such as declaring variables in places other than a function's start.

@bscottm
Copy link
Contributor

bscottm commented Jul 25, 2025

I ended up with some research time this afternoon, queried Perplexity.ai, which turns out to be quite useful and didn't turn out "botshit".

Here's what I suggest, pending your concurrence:

#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
#  if _MSC_VER >= 1900 || defined(__USE_MINGW_ANSI_STDIO)
/* VS 2015 or later, MinGW with ANSI stdio: Use the standard. Don't assume that
 * MS or MinGW will support the "I" qualifier indefinitely. */
#    define SIZE_T_FMT   "z"
#    define T_UINT64_FMT "j"
#    define T_INT64_FMT  "j"
#  elif (_MSC_VER < 1900 && defined(_WIN64)) || defined(__MINGW64__)
/* VS, 64-bit: use Microsoft's "I" qualifier.  */
#    define SIZE_T_FMT   "I64"
#    define T_UINT64_FMT "I64"
#    define T_INT64_FMT  "I64"
#  elif (_MSC_VER < 1900 && defined(_WIN32)) || defined(__MINGW32__)
#    define SIZE_T_FMT   "I32"
#    define T_UINT64_FMT "I32"
#    define T_INT64_FMT  "I32"
#endif

Side note: According to Perplexity, VS 2010 will accept variable declarations anywhere, not just at the top of the function. So, there's a good chance the libslirp code will compile.

@cmgauger
Copy link
Contributor Author

I can agree with just about all of that change, except that even on 32-bit platforms the T_UINT64_FMT and T_INT64_FMT should be I64.

Because int64_t/uint64_t are 64-bit.

So altogether the new conditional block would be:

#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)
#  if _MSC_VER >= 1900 || defined(__USE_MINGW_ANSI_STDIO)
/* VS 2015 or later, MinGW with ANSI stdio: Use the standard. Don't assume that
 * MS or MinGW will support the "I" qualifier indefinitely. */
#    define SIZE_T_FMT   "z"
#    define T_UINT64_FMT "j"
#    define T_INT64_FMT  "j"
#  elif (_MSC_VER < 1900 && defined(_WIN64)) || defined(__MINGW64__)
/* VS, 64-bit: use Microsoft's "I" qualifier.  */
#    define SIZE_T_FMT   "I64"
#    define T_UINT64_FMT "I64"
#    define T_INT64_FMT  "I64"
#  elif (_MSC_VER < 1900 && defined(_WIN32)) || defined(__MINGW32__)
#    define SIZE_T_FMT   "I32"
#    define T_UINT64_FMT "I64"
#    define T_INT64_FMT  "I64"
#endif

As with regards to the C standard VS 2010 (and older) support, Herb Sutter mentioned this on his blog (in 2012):

VC++ 2010 already fully supports the C subset of C++98, including things like <stdint.h> and declarations in the middle of a block.[*] The C subset of C++98 is approximately C95 (with very few incompatibilities with C95; i.e., there are very few cases where legal C95 code has a different meaning or is invalid in C++98) plus a few C99 features like declaring variables in the middle of blocks).

My own experience is that a lot of C99 code just works. But there's the occasional bit that blows up (oh hi there stdbool.h).

Unrelated to Visual Studio, but as you mentioned bringing in C99 libraries: That'll break the VAX build, because the HP C V6.4-005 compiler is not C99.

From the vim v9.1 Reference Manual (which is from all of five days ago as of writing this -- July 22, 2025):

C COMPILER assumptions-C-compiler
ANSI-C C89 C90 C95 C99

Vim strives for maximum portability (see design-multi-platform and must still build with Compaq C V6.4-005 on OpenVMS VAX V7.3.

Therefore, the latest ISO C standard we follow is:

C95 (ISO/IEC 9899:1990/AMD1:1995)

In addition, the following two C99 features are explicitly allowed:
– // comments, as required by style-comments;
– the _Bool type.

Platform-specific code may use any newer compiler features supported on that platform.

But that's an entirely different topic, unrelated to this PR.

Updated with bscottm's suggested fixes to keep ANSI syntax for VS 2015
and later.
@bscottm
Copy link
Contributor

bscottm commented Jul 29, 2025

I can agree with just about all of that change, except that even on 32-bit platforms the T_UINT64_FMT and T_INT64_FMT should be I64.

My bad. Even I get things wrong from time to time. :-) :-) :-)

Unrelated to Visual Studio, but as you mentioned bringing in C99 libraries: That'll break the VAX build, because the HP C V6.4-005 compiler is not C99.

Not much I can do about that -- I definitely can't ask the libslirp developer/maintainer to regress (been there, done that, "Get a newer compiler!") libslirp is what it is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants