
Hello! When compiling ghc-6.6.1 on SunOS 5.9 I get the following error message
posix/OSMem.c:20:20: error: stdint.h: No such file or directory gmake[1]: *** [posix/OSMem.o] Error 1 gmake[1]: *** Waiting for unfinished jobs....
GC.c:1870:0: warning: 'regparm' attribute directive ignored gmake: *** [stage1] Error 1
stdint.h seems to be unavailable on solaris 9. Looking at the
rts/posix/OSMem.c file we find
/* no C99 header stdint.h on OpenBSD? */
#if defined(openbsd_HOST_OS)
typedef unsigned long my_uintptr_t;
#else
#include

On Friday 14 September 2007 16:33, Robert Andersson wrote:
stdint.h seems to be unavailable on solaris 9. Looking at the rts/posix/OSMem.c file we find
/* no C99 header stdint.h on OpenBSD? */ #if defined(openbsd_HOST_OS) typedef unsigned long my_uintptr_t; #else #include
typedef uintptr_t my_uintptr_t; #endif
Uh, oh... Using long doesn't look very 64bit-safe (it could be int, long, long
long, who knows?). IIRC some systems without

On Sat, 2007-09-15 at 11:09 +0200, Sven Panne wrote:
On Friday 14 September 2007 16:33, Robert Andersson wrote:
stdint.h seems to be unavailable on solaris 9. Looking at the rts/posix/OSMem.c file we find
/* no C99 header stdint.h on OpenBSD? */ #if defined(openbsd_HOST_OS) typedef unsigned long my_uintptr_t; #else #include
typedef uintptr_t my_uintptr_t; #endif Uh, oh... Using long doesn't look very 64bit-safe (it could be int, long, long long, who knows?). IIRC some systems without
provide at least , so we should perhaps use this as a fallback?
The RIGHT way to do this is rather messy .. but there is only one right way to do it. 1. Measure the size (and alignment, while at it) of all the integer types. (trial execute and run). 2. Find out which headers are present (trial compilation) 3. For stdint.h if present, detect which typedefs are provided. (trial compilation) 4. For the ones provided, AND size_t, ptrdiff_t, check their size (and signedness). (trial execution) 5. For the fundamental types, write a set of C++ functions overloaded on the types, which print which one was chosen. Yes, this step is mandatory, and it must be done in C++. 6. Test what the typedefs found are actually aliased to using C++ (there is no other portable way to do this). (Trial execution) 7. NOW, define YOUR OWN typedefs and macros to model C99 types when available, otherwise whatever else you have otherwise pick something. There is a (usually valid) assumption that the C and C++ compilers agree (and you actually have a C++ variant). With that caveat, the processes above will work on a hosted environment (only), to determine the correct bindings for symbols without ANY knowledge of the underlying OS or C/C++ compiler: there's no #if rubbish involved based on the OS. You end up with 'my_int32_t' 'my_intptr_t' etc etc, which provide the guarantee that * they alias any related symbol actually provided by the system * they're always present * they're well defined even if there is no long long or long double (or _complex etc) types * the exact integers type my_(u)int<N>_t are provided for N = 8,16,32,64 on all machines. To ensure the N=64 case, an emulation library may be needed. It is vital that application software DOES NOT define any of the standard symbols. NEVER NEVER define intptr_t for example, even if it is undefined. It's horrible. But the above is utterly essential and it is the ONLY correct solution. Prebuild configurations can of course be used .. the algorithm above can used to prepare them when possible. If you have cross compilation situation, only half the autoconfig process works (trial compilation, but not trial execution). This kind of test: #if defined(openbsd_HOST_OS) typedef unsigned long my_uintptr_t; is almost always wrong. Integers sizes are not determined by the host OS. They're also not determined by the processor. They're also not determined by the (generic) compiler kind either. Such tests are fine for guessing what to test, but don't replace actually doing the tests. Please *don't* make the mistake of trying to use #conditional compilation in C code based on supplied macros (_WIN32 etc) to make choices. Ocaml is currently broken on XP64 for that reason: the compiler works .. all the C libraries are bugged due to the wrong assumption 'long' was the size of a pointer. [In 64 bit Windows .. it's 'long long'] Don't make assumptions .. use type names defined by Haskell with specific semantics .. then calculate them in the config script. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net

On Saturday 15 September 2007 13:58, skaller wrote:
The RIGHT way to do this is rather messy .. but there is only one right way to do it. [...]
IMHO things are not that black or white: I think we have a tradeoff between
almost 100% fool-proofness (which is basically the approach you describe) and
the ability to do cross-compilation (use available headers and perhaps
fail/guess if nothings sensible could be found). What is "right" has to be
decided on a case-by-case basis.
But of course you are totally right in one respect: OS-based tests when used
in a context like our example are silly and should be replaced by
feature-based tests (be it "Do we have

On Sat, 2007-09-15 at 14:42 +0200, Sven Panne wrote:
On Saturday 15 September 2007 13:58, skaller wrote:
The RIGHT way to do this is rather messy .. but there is only one right way to do it. [...]
IMHO things are not that black or white: I think we have a tradeoff between almost 100% fool-proofness (which is basically the approach you describe) and the ability to do cross-compilation (use available headers and perhaps fail/guess if nothings sensible could be found). What is "right" has to be decided on a case-by-case basis.
Of course.
But of course you are totally right in one respect: OS-based tests when used in a context like our example are silly and should be replaced by feature-based tests (be it "Do we have
?" or "What is the result of compiling/running blah.c?").
I think the key point is that if you need say int32_t or intptr_t you must not use them, but instead use my_int_32_t or my_intptr_t size_t is required by ISO C89, C99 and C++ Standards so can be used directly. This is annoying, but if you use these in interfaces defining your own is necessary to avoid conflicts. In implementations there's more freedom .. but no point not using the 'my_' versions. In effect, this removes the 'optional' status of the symbols and creates an in-house de-facto standard you can rely on. Pity ISO didn't do that originally .. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net

On Saturday 15 September 2007 13:58, skaller wrote:
[...] 1. Measure the size (and alignment, while at it) of all the integer types. (trial execute and run). [...] 4. For the ones provided, AND size_t, ptrdiff_t, check their size (and signedness). (trial execution)
Small additional note: One can determine the size and alignment *without* always resorting to trial execution. Autoconf does this by an ingenious "evaluation by compilation" technique in case of cross-compilation, see the implementation of AC_CHECK_SIZEOF and AC_CHECK_ALIGNOF.
[...] 6. Test what the typedefs found are actually aliased to using C++ (there is no other portable way to do this). (Trial execution)
To be honest, I don't understand that point... Cheers, S.

On Sat, 2007-09-15 at 15:42 +0200, Sven Panne wrote:
On Saturday 15 September 2007 13:58, skaller wrote:
[...] 1. Measure the size (and alignment, while at it) of all the integer types. (trial execute and run). [...] 4. For the ones provided, AND size_t, ptrdiff_t, check their size (and signedness). (trial execution)
Small additional note: One can determine the size and alignment *without* always resorting to trial execution. Autoconf does this by an ingenious "evaluation by compilation" technique in case of cross-compilation, see the implementation of AC_CHECK_SIZEOF and AC_CHECK_ALIGNOF.
Hmm .. I guess that's possible with some kind of type checking hack? .. (I don't use autoconf but thanks for that info .. I will have a look, it would be cool to get better cross-compilation support).
[...] 6. Test what the typedefs found are actually aliased to using C++ (there is no other portable way to do this). (Trial execution)
To be honest, I don't understand that point...
The point is that on most systems knowing the size of say 'intptr_t' does NOT tell you which integer type it is aliased to if there are two of them, eg: // gcc, amd64, Linux: sizeof(intptr_t) == 8 sizeof(long) == 8 sizeof(long long)== 8 sizeof(int)=4 so intptr_t is aliased to long or long long .. which one? Short of examining the header file source code, the only way to find out is in C++ with overloading: void me(long){ printf("long"); } void me(long long) { printf("long long"); } me( (intptr_t)0 ); Unfortunately EVERY system I know of has at least one more integer type available than actual integer sizes: on Win64 int and long have the same size, and of course long long must be used for the "integer the size of a pointer". This is what bit the Ocaml library.. if you need such an animal it MUST be called 'my_intptr_t', since no other name is standard than one you define yourself. Yeah, this is a REAL pain .. but look at any 'professional' C library (OpenGL, GMP, etc etc) and they all do it. BTW: in C, it usually doesn't matter which of two integers types of the same size you use .. but in C++ it does, precisely because it affects overloading. But note C99 has 'overloading' of a kind, and consider using "C++ safe" C when possible, I think it is worth ensuring 'my_intptr_t' aliases the same type as 'intptr_t' if the latter is defined. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net

skaller wrote:
On Sat, 2007-09-15 at 11:09 +0200, Sven Panne wrote:
On Friday 14 September 2007 16:33, Robert Andersson wrote:
stdint.h seems to be unavailable on solaris 9. Looking at the rts/posix/OSMem.c file we find
/* no C99 header stdint.h on OpenBSD? */ #if defined(openbsd_HOST_OS) typedef unsigned long my_uintptr_t; #else #include
typedef uintptr_t my_uintptr_t; #endif Uh, oh... Using long doesn't look very 64bit-safe (it could be int, long, long long, who knows?). IIRC some systems without provide at least , so we should perhaps use this as a fallback? The RIGHT way to do this is rather messy .. but there is only one right way to do it.
1. Measure the size (and alignment, while at it) of all the integer types. (trial execute and run).
We already do this. Incedentally, the GHC RTS does provide a full complement of explicitly-sized types: Stg{Int,Word}{8,16,32,64} which are correct on all the platforms we currently support. The header that defines them currently makes some assumptions (e.g. char is 8 bits), but that's only because we're a bit lazy; it wouldn't be hard to fix it if we need to port to a platform on which it isn't true. I've just fixed OSMem.c to use StgWord (the pointer-sized integral type) instead of my_uintptr_t, I'm validating the fix now. Cheers, Simon

On Mon, 2007-09-17 at 13:03 +0100, Simon Marlow wrote:
skaller wrote:
1. Measure the size (and alignment, while at it) of all the integer types. (trial execute and run).
We already do this. Incedentally, the GHC RTS does provide a full complement of explicitly-sized types: Stg{Int,Word}{8,16,32,64} which are correct on all the platforms we currently support. The header that defines them currently makes some assumptions (e.g. char is 8 bits), but that's only because we're a bit lazy;
All good programmers are lazy -- the whole point of computers is to automate things so we can be :) The algorithm I presented (crudely) goes further. What you're doing is enough for a language that doesn't have to bind to C and/or C++. Note that doesn't mean you can't generate C/C++, it means you don't need to bind to existing libraries. If you did, you need to do more work as described. For example if there is a C lib requiring a int* argument .. there's no way to tell which one of your types, if any, is aliased to it. Of course .. this only matters in glue logic, i.e. when you're actually writing C/C++ code, not if you're generating assembler/machine code and calling the library directly using the platform ABI based on registers. When writing glue logic the end user can fix this problem with platform specific casts, but platform *independent* casts would be better (e.g. you might cast StgInt32 pointer to long* on Win64.. but that would make a corey mess if the glue logic were compiled on Linux where long is 64 bits). In Felix, this is critical, because the compiler actually generates C++ which has to run on all platforms. I actually preferred the same model you're using: (u)int{8,16,32,64} but it just didn't fly because of the almost complete dependence on source code based binding (Felix is roughly a well typed souped up macro processor .. :) BTW: .. any thoughts on 128 bit integers? I mean, registers are now 64 bits on desktops. There really should be a double precision integer type.. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net

On Monday 17 September 2007 18:14, skaller wrote:
[...] The algorithm I presented (crudely) goes further. What you're doing is enough for a language that doesn't have to bind to C and/or C++. [...]
I think this is an exaggeration, we do bind to C and do this quite successfully. As long as C has variable argument lists, void* and char* (potentially aliasing *every* data type, this is explicit in the C standards) all over the place in tons of OS calls and library entries, your fine-grained distinction is a bit academic. Look e.g. at dlopen(): It returns a void*, even for functions, which is theoretically wrong, but this doesn't stop *nices from working. ;-) As long as things like this are the case (i.e. forever), C will never have some real overloading, because that would mean invalidating almost every SW out there. C is basically a slighty typed assembler language, nothing more. Granted, the story is different for languages like C++, where type signatures are even manifest in the object files, but we do not bind to C++. Cheers, S.
participants (4)
-
Robert Andersson
-
Simon Marlow
-
skaller
-
Sven Panne