/* rpmtypes.h This is the "new" rpmtypes, created for GCC compilers under Linux (Pentium-III or later) and MacOS X. The assumption is that "short" is s16, "int" is s32, and "long long" is legal and gives s64. No assumption is made about "long". If you are in a system that does not have a compiler-defined 64-bit type (like PMZ in Metrowerks) use the old "rpm_types.h" instead. REVISION HISTORY: 2009021x Add #define U8, S8, U16, etc. for use by AltiVec vector macros. 20091111 Add f23e7 and f52e11 types 20091113 Add float4 and float8 20130225 Use type_s16.h, type_s32.h and type_s64.h; expand the documentation of 128-bit integers and GCC. BUGS and TTD I need to test this under my rpmlib.pl &mk_compile function in both 32 and 64 modes to find out if the assumptions are still correct. NOTES Compiler-provided definitions can be seen with "show_predefs.h" or a command like: gcc -dM -E - < /dev/null | sort Here is a partial list of compiler defines that can be used to figure out the size of pointer and long. They are used like this: #if (defined(__foo__) && defined(__bar__)) width defines 64 _AIX && __64BIT__ 64 __hpux && __LP64__ 32 __linux__ && __i386__ 64 __linux__ && __ia64__ 64 __linux__ && __x86_64__ 32 __linux__ && __powerpc__ 64 __linux__ && __powerpc64__ 32 __linux__ && __s390__ 64 __linux__ && __s390x__ 64 __osf__ 64 sinix && __LP64__ 64 _WIN64 32 _WIN32 For Sun you need to test for either "sun" or "__sun", and if either of these is defined, include then test for "_LP64". To get a 64-bit int in Microsoft visual C++ (i.e. in both _WIN32 and _WIN64), you can use "__int64" and "unsigned __int64". In the latter csae (only) __int64 is equivlent to long. To get 64-bit int in Linux (i.e. __linux__) and in MacOS X (i.e. __APPLE__ && __MACH__) you can include and then use "int64_t" and "uint64_t". Otherwise test the other combinations in the list above (e.g. __hpux && __LP64__) to find out if "long" is 64-bit, and if it isn't, use "long long" to get 64-bit. REGARDING 128-BIT INTEGERS In early versions of the GCC extentions documentation, the section defining 'long long int' was as follows (taken from the info node "(gcc.info)Long Long" from GNU CC version 2.7.2): Double-Word Integers ==================== GNU C supports data types for integers that are twice as long as `long int'. Simply write `long long int' for a signed integer, or `unsigned long long int' for an unsigned integer. To make an integer constant of type `long long int', add the suffix `LL' to the integer. To make an integer constant of type `unsigned long long int', add the suffix `ULL' to the integer. You can use these types in arithmetic like any other integer types. Addition, subtraction, and bitwise boolean operations on these types are open-coded on all types of machines. Multiplication is open-coded if the machine supports fullword-to-doubleword a widening multiply instruction. Division and shifts are open-coded only on machines that provide special support. The operations that are not open-coded use special library routines that come with GNU CC. There may be pitfalls when you use `long long' types for function arguments, unless you declare function prototypes. If a function expects type `int' for its argument, and you pass a value of type `long long int', confusion will result because the caller and the subroutine will disagree about the number of bytes for the argument. Likewise, if the function expects `long long int' and you pass `int'. The best way to avoid such problems is to use prototypes. If you combine this with the original C language definition of 'long int' (i.e. the widest integer type supported by the hardware registers) it implies that on future 64-bit targets (such as the DEC Alpha 21064) GCC would provide 128-bit integers via 'long long int'. (Presumably on such a machine, 'int' would be 32-bit and 'short' would be 16-bit). However, by the time 64-bit processors began to be fairly popular (around when Athlon 64 became widely deployed) it became clear that it was more important to have a standard way to get 64-bit integers, so GCC changed course. Later versions of the first paragraph above read like this example from GCC version 2.95: GNU C supports data types for integers that are <#:twice as long as int:>. Simply write long long int for a signed integer, or unsigned long long int for an unsigned integer. To make an integer constant of type long long int, add the suffix LL to the integer. To make an integer constant of type unsigned long long int, add the suffix ULL to the integer. This rewriting reflects the current GCC situation in which `int' is 32-bit and `long long int' is 64-bit regardless of whether you compile for 32-bit or 64-bit (e.g. using -m32 or -m64 respectively: the difference is still visible in the size of `long' or of a pointer, e.g. "sizeof(void *)". `long' by definition is always the same as the size of a pointer.) Still later versions make it even more explicit; this text is from GCC version 3.0.4: <#:ISO C99 supports data types for integers that are at least 64 bits wide, and as an extension GCC supports them in C90 mode and in C++.:> Simply write long long int for a signed integer, or unsigned long long int for an unsigned integer. To make an integer constant of type long long int, add the suffix `LL' to the integer. To make an integer constant of type unsigned long long int, add the suffix `ULL' to the integer. Much later GCC versions added a separate section for __int128, here showing the text from the documentation of GCC version 4.6.3: 6.8 128-bit integers As an extension the integer scalar type __int128 is supported for targets having an integer mode wide enough to hold 128-bit. Simply write __int128 for a signed 128-bit integer, or unsigned __int128 for an unsigned 128-bit integer. There is no support in GCC to express an integer constant of type __int128 for targets having long long integer with less then 128 bit width. I have also discovered that GCC actually implements a __int128_t type on 64-bit PowerPC and Intel targets (at least as far back as GCC 4.0.1, which I have on PMG5 and MBP). This "__int128_t" is not widely discussed online, nor any explanation of why it's not "__int128". Also, newer versions of GCC have an explicit width declaration syntax like this: typedef unsigned int u64 __attribute__((mode(DI))); which references the internal GCC "machine modes", which include: QImode - "Quarter-Integer" mode represents a single byte treated as an integer. HImode - "Half-Integer" mode represents a two-byte integer. SImode - "Single Integer" mode represents a four-byte integer. DImode - "Double Integer" mode represents an eight-byte integer. TImode - "Tetra Integer" (?) mode represents a sixteen-byte integer. OImode - "Octa Integer" (?) mode represents a thirty-two-byte integer. Here is a relevant quote: http://www.x86-64.org/pipermail/discuss/2005-August/006412.html Jan Beulich JBeulich at novell.com Thu Aug 18 15:37:17 CEST 2005 >>> "H. Peter Anvin" 11.08.05 01:50:28 >>> >Gunnar wrote: >> Hi. >> I'd like to multiply two 64 bit integers and run it on a Athlon64 machine. >> That means the result will be 128 bits. >> Is there any 128 bit integer arithmetic instructions that I could use or do I >> have to find other ways of doing it? >> >> Any illustrative code in C/C++ could also help a lot. >> > >I don't think __int128 is implemented in the current gcc, but in >assembly this is simply done with the mulq (or, if signed, imulq) >instruction: gcc supports double-machine-word operations generically for all targets, so 128-bit integer support (using the custom __int128_t and __uint128_t types, or using __attribute__((__mode__(__TI__)))) is available for all 64-bit machines. Additionally I recently committed a patch to actually provide some backend support for x86-64, so 4.1 will hopefully produce somewhat better code than that resulting from the (architecture independent) splitting into machine word quantities. Jan Most of the documentation for old versions of GCC is at gcc.gnu.org/onlinedocs/ */ #ifndef _RPMTYPES_H_ #define _RPMTYPES_H_ /* 'bit' is for boolean flags, just to be generous we give it an 8-bit signed range. */ typedef char bit; typedef char s8; typedef unsigned char u8; #include "type_s16.h" /* typedef short s16; typedef unsigned short u16; */ #include "type_s32.h" /* typedef int s32; typedef unsigned int u32; */ /* In the 1990's (Metrowerks projects, notably Power MANDELZOOM) I used the C++ library "i64_o.c" to get 64-bit integers. PMZ is the only project I have that still uses it. "long long" has been available since I switched to Linux. */ #include "type_s64.h" /* typedef long long int s64; typedef unsigned long long int u64; */ #ifdef RPMTYPES_WANT_128 # if (defined(__LP64__) || defined(__x86_64__) || defined(__powerpc64__) || defined(_M_X64) || defined(_M_IX86)) # define RPMTYPES_128_AVAILABLE typedef __int128_t s128; typedef __uint128_t u128; # else /* For environments without __int128_t, 128-bit integers are provided by the operator-overloading object .../include/int128.c and which requires converting your program to C++. Read that file for more details. The data type is equivalent to: typedef struct { s64 hi; u64 lo; } s128; typedef struct { u64 hi; u64 lo; } u128; */ # define s128 use_int128_dot_c use_int128_dot_c; # define u128 use_int128_dot_c use_int128_dot_c; # endif #endif typedef float f32; typedef double f64; typedef float f23e7; typedef double f52e11; typedef float float4; typedef double float8; /* These #defines can be used in a similar way to the similarly named * typedefs. However, they can also be used inside vector * declarations, as in "vector S16 foo;". Normal typedefs like * "typedef short s16" don't work this way, if you declare "vector s16 * foo;" you'll get an error. */ #define U8 unsigned char #define S8 signed char #define U16 unsigned short #define S16 signed short #define U32 unsigned int #define S32 signed int #define U64 unsigned long long #define S64 signed long long #define F23E7 float #define F52E11 double #endif /* _RPMTYPES_H_ */